From fbffd80fb2ba16c68f799da68a119d5e69643604 Mon Sep 17 00:00:00 2001 From: Rui Paulo Date: Sat, 18 Apr 2015 05:04:12 +0000 Subject: Vendor import of wpa_supplicant/hostapd 2.4. Major changes are: SAE, Suite B, RFC 7268, EAP-PKE, ACS, and tons of bug fixes. --- src/Makefile | 2 +- src/ap/Makefile | 2 +- src/ap/accounting.c | 68 +- src/ap/acs.c | 949 +++ src/ap/acs.h | 27 + src/ap/ap_config.c | 427 +- src/ap/ap_config.h | 164 +- src/ap/ap_drv_ops.c | 137 +- src/ap/ap_drv_ops.h | 137 +- src/ap/ap_list.c | 108 +- src/ap/ap_list.h | 22 +- src/ap/ap_mlme.c | 6 +- src/ap/authsrv.c | 17 +- src/ap/beacon.c | 487 +- src/ap/beacon.h | 19 +- src/ap/bss_load.c | 65 + src/ap/bss_load.h | 17 + src/ap/ctrl_iface_ap.c | 334 +- src/ap/ctrl_iface_ap.h | 7 +- src/ap/dfs.c | 1064 +++ src/ap/dfs.h | 30 + src/ap/dhcp_snoop.c | 178 + src/ap/dhcp_snoop.h | 30 + src/ap/drv_callbacks.c | 603 +- src/ap/eap_user_db.c | 16 +- src/ap/gas_serv.c | 494 +- src/ap/gas_serv.h | 19 +- src/ap/hostapd.c | 1804 ++++- src/ap/hostapd.h | 172 +- src/ap/hs20.c | 154 +- src/ap/hs20.h | 8 +- src/ap/hw_features.c | 801 +- src/ap/hw_features.h | 16 +- src/ap/iapp.c | 84 +- src/ap/ieee802_11.c | 1156 ++- src/ap/ieee802_11.h | 32 +- src/ap/ieee802_11_auth.c | 38 +- src/ap/ieee802_11_ht.c | 278 +- src/ap/ieee802_11_shared.c | 136 +- src/ap/ieee802_11_vht.c | 207 +- src/ap/ieee802_1x.c | 692 +- src/ap/ieee802_1x.h | 1 + src/ap/ndisc_snoop.c | 171 + src/ap/ndisc_snoop.h | 36 + src/ap/p2p_hostapd.c | 5 +- src/ap/peerkey_auth.c | 22 +- src/ap/pmksa_cache_auth.c | 180 +- src/ap/pmksa_cache_auth.h | 8 + src/ap/sta_info.c | 329 +- src/ap/sta_info.h | 73 +- src/ap/tkip_countermeasures.c | 8 +- src/ap/vlan_init.c | 114 +- src/ap/vlan_init.h | 10 +- src/ap/wmm.c | 12 +- src/ap/wmm.h | 10 +- src/ap/wnm_ap.c | 357 +- src/ap/wnm_ap.h | 15 +- src/ap/wpa_auth.c | 590 +- src/ap/wpa_auth.h | 41 +- src/ap/wpa_auth_ft.c | 351 +- src/ap/wpa_auth_glue.c | 88 +- src/ap/wpa_auth_i.h | 39 +- src/ap/wpa_auth_ie.c | 161 +- src/ap/wpa_auth_ie.h | 7 + src/ap/wps_hostapd.c | 508 +- src/ap/wps_hostapd.h | 9 + src/ap/x_snoop.c | 123 + src/ap/x_snoop.h | 56 + src/common/Makefile | 2 +- src/common/common_module_tests.c | 172 + src/common/defs.h | 92 +- src/common/eapol_common.h | 13 +- src/common/hw_features_common.c | 438 + src/common/hw_features_common.h | 40 + src/common/ieee802_11_common.c | 469 +- src/common/ieee802_11_common.h | 33 +- src/common/ieee802_11_defs.h | 410 +- src/common/ieee802_1x_defs.h | 78 + src/common/privsep_commands.h | 2 + src/common/qca-vendor-attr.h | 28 + src/common/qca-vendor.h | 246 + src/common/sae.c | 1069 +++ src/common/sae.h | 67 + src/common/tnc.h | 121 + src/common/version.h | 2 +- src/common/wpa_common.c | 586 +- src/common/wpa_common.h | 121 +- src/common/wpa_ctrl.c | 134 +- src/common/wpa_ctrl.h | 142 +- src/common/wpa_helpers.c | 292 + src/common/wpa_helpers.h | 37 + src/crypto/Makefile | 4 +- src/crypto/aes-ccm.c | 2 +- src/crypto/aes-eax.c | 2 +- src/crypto/aes-gcm.c | 2 +- src/crypto/aes-omac1.c | 64 +- src/crypto/aes-siv.c | 188 + src/crypto/aes-unwrap.c | 19 +- src/crypto/aes-wrap.c | 20 +- src/crypto/aes_siv.h | 19 + src/crypto/aes_wrap.h | 15 +- src/crypto/crypto.h | 323 +- src/crypto/crypto_internal-rsa.c | 9 + src/crypto/crypto_module_tests.c | 1679 ++++ src/crypto/crypto_nss.c | 207 - src/crypto/crypto_openssl.c | 635 +- src/crypto/dh_groups.c | 674 +- src/crypto/dh_groups.h | 3 + src/crypto/fips_prf_cryptoapi.c | 19 - src/crypto/fips_prf_gnutls.c | 20 - src/crypto/fips_prf_nss.c | 19 - src/crypto/md5.c | 6 +- src/crypto/milenage.c | 4 +- src/crypto/ms_funcs.c | 1 + src/crypto/random.c | 12 +- src/crypto/sha1-internal.c | 2 + src/crypto/sha1-prf.c | 1 + src/crypto/sha1.c | 5 +- src/crypto/sha256-kdf.c | 76 + src/crypto/sha256-prf.c | 40 +- src/crypto/sha256.h | 8 +- src/crypto/sha384.h | 19 + src/crypto/tls.h | 47 +- src/crypto/tls_gnutls.c | 939 ++- src/crypto/tls_internal.c | 55 + src/crypto/tls_none.c | 6 + src/crypto/tls_nss.c | 645 -- src/crypto/tls_openssl.c | 1053 ++- src/crypto/tls_schannel.c | 31 + src/drivers/Makefile | 2 +- src/drivers/android_drv.h | 8 +- src/drivers/driver.h | 1970 +++-- src/drivers/driver_atheros.c | 362 +- src/drivers/driver_bsd.c | 241 +- src/drivers/driver_common.c | 154 +- src/drivers/driver_hostap.c | 50 +- src/drivers/driver_macsec_qca.c | 891 +++ src/drivers/driver_madwifi.c | 1313 --- src/drivers/driver_ndis.c | 42 +- src/drivers/driver_nl80211.c | 13147 +++++++++++++++---------------- src/drivers/driver_nl80211.h | 274 + src/drivers/driver_nl80211_android.c | 220 + src/drivers/driver_nl80211_capa.c | 1532 ++++ src/drivers/driver_nl80211_event.c | 2029 +++++ src/drivers/driver_nl80211_monitor.c | 491 ++ src/drivers/driver_nl80211_scan.c | 775 ++ src/drivers/driver_none.c | 8 - src/drivers/driver_openbsd.c | 136 + src/drivers/driver_privsep.c | 31 +- src/drivers/driver_roboswitch.c | 29 +- src/drivers/driver_test.c | 3315 -------- src/drivers/driver_wext.c | 285 +- src/drivers/driver_wext.h | 6 - src/drivers/driver_wired.c | 119 +- src/drivers/drivers.c | 36 +- src/drivers/drivers.mak | 43 +- src/drivers/drivers.mk | 28 +- src/drivers/linux_defines.h | 46 + src/drivers/linux_ioctl.c | 7 +- src/drivers/linux_wext.h | 12 +- src/drivers/netlink.c | 42 +- src/drivers/nl80211_copy.h | 1454 +++- src/drivers/priv_netlink.h | 2 + src/eap_common/Makefile | 2 +- src/eap_common/eap_common.c | 85 +- src/eap_common/eap_common.h | 12 +- src/eap_common/eap_defs.h | 42 +- src/eap_common/eap_eke_common.c | 768 ++ src/eap_common/eap_eke_common.h | 114 + src/eap_common/eap_fast_common.c | 2 +- src/eap_common/eap_fast_common.h | 2 +- src/eap_common/eap_gpsk_common.c | 144 +- src/eap_common/eap_gpsk_common.h | 6 + src/eap_common/eap_ikev2_common.c | 12 +- src/eap_common/eap_ikev2_common.h | 7 - src/eap_common/eap_pax_common.c | 8 +- src/eap_common/eap_pax_common.h | 3 +- src/eap_common/eap_pwd_common.c | 17 +- src/eap_common/eap_pwd_common.h | 2 +- src/eap_common/eap_sim_common.c | 13 +- src/eap_common/eap_sim_common.h | 3 +- src/eap_common/ikev2_common.c | 89 +- src/eap_common/ikev2_common.h | 4 - src/eap_peer/Makefile | 2 +- src/eap_peer/eap.c | 639 +- src/eap_peer/eap.h | 33 +- src/eap_peer/eap_aka.c | 170 +- src/eap_peer/eap_config.h | 113 +- src/eap_peer/eap_eke.c | 765 ++ src/eap_peer/eap_fast.c | 74 +- src/eap_peer/eap_fast_pac.c | 36 +- src/eap_peer/eap_gpsk.c | 86 +- src/eap_peer/eap_i.h | 37 +- src/eap_peer/eap_ikev2.c | 72 +- src/eap_peer/eap_leap.c | 5 +- src/eap_peer/eap_methods.c | 4 +- src/eap_peer/eap_methods.h | 2 + src/eap_peer/eap_mschapv2.c | 52 +- src/eap_peer/eap_pax.c | 32 +- src/eap_peer/eap_peap.c | 139 +- src/eap_peer/eap_proxy.h | 49 + src/eap_peer/eap_proxy_dummy.c | 77 + src/eap_peer/eap_psk.c | 29 +- src/eap_peer/eap_pwd.c | 128 +- src/eap_peer/eap_sake.c | 27 +- src/eap_peer/eap_sim.c | 162 +- src/eap_peer/eap_tls.c | 111 +- src/eap_peer/eap_tls_common.c | 78 +- src/eap_peer/eap_tls_common.h | 4 + src/eap_peer/eap_tnc.c | 3 +- src/eap_peer/eap_ttls.c | 93 +- src/eap_peer/eap_vendor_test.c | 21 +- src/eap_peer/eap_wsc.c | 75 +- src/eap_peer/ikev2.c | 65 +- src/eap_peer/mschapv2.c | 4 +- src/eap_peer/tncc.c | 61 +- src/eap_server/Makefile | 2 +- src/eap_server/eap.h | 33 +- src/eap_server/eap_i.h | 32 +- src/eap_server/eap_methods.h | 2 + src/eap_server/eap_server.c | 608 +- src/eap_server/eap_server_aka.c | 39 +- src/eap_server/eap_server_eke.c | 793 ++ src/eap_server/eap_server_fast.c | 46 +- src/eap_server/eap_server_gpsk.c | 68 +- src/eap_server/eap_server_gtc.c | 2 +- src/eap_server/eap_server_identity.c | 7 + src/eap_server/eap_server_ikev2.c | 55 +- src/eap_server/eap_server_md5.c | 2 +- src/eap_server/eap_server_methods.c | 4 +- src/eap_server/eap_server_mschapv2.c | 34 +- src/eap_server/eap_server_pax.c | 42 +- src/eap_server/eap_server_peap.c | 156 +- src/eap_server/eap_server_psk.c | 49 +- src/eap_server/eap_server_pwd.c | 114 +- src/eap_server/eap_server_sake.c | 66 +- src/eap_server/eap_server_sim.c | 36 +- src/eap_server/eap_server_tls.c | 72 +- src/eap_server/eap_server_tls_common.c | 70 + src/eap_server/eap_server_tnc.c | 3 +- src/eap_server/eap_server_ttls.c | 80 +- src/eap_server/eap_server_wsc.c | 2 +- src/eap_server/eap_sim_db.c | 56 +- src/eap_server/eap_tls_common.h | 4 + src/eap_server/ikev2.c | 4 +- src/eap_server/tncs.c | 79 +- src/eapol_auth/Makefile | 2 +- src/eapol_auth/eapol_auth_dump.c | 268 +- src/eapol_auth/eapol_auth_sm.c | 107 +- src/eapol_auth/eapol_auth_sm.h | 16 +- src/eapol_auth/eapol_auth_sm_i.h | 8 + src/eapol_supp/Makefile | 2 +- src/eapol_supp/eapol_supp_sm.c | 275 +- src/eapol_supp/eapol_supp_sm.h | 60 +- src/l2_packet/Makefile | 2 +- src/l2_packet/l2_packet.h | 30 + src/l2_packet/l2_packet_freebsd.c | 19 + src/l2_packet/l2_packet_linux.c | 247 +- src/l2_packet/l2_packet_ndis.c | 19 + src/l2_packet/l2_packet_none.c | 22 +- src/l2_packet/l2_packet_pcap.c | 18 +- src/l2_packet/l2_packet_privsep.c | 36 +- src/l2_packet/l2_packet_winpcap.c | 12 + src/lib.rules | 4 + src/p2p/Makefile | 3 +- src/p2p/p2p.c | 2316 ++++-- src/p2p/p2p.h | 514 +- src/p2p/p2p_build.c | 348 +- src/p2p/p2p_dev_disc.c | 88 +- src/p2p/p2p_go_neg.c | 655 +- src/p2p/p2p_group.c | 214 +- src/p2p/p2p_i.h | 192 +- src/p2p/p2p_invitation.c | 382 +- src/p2p/p2p_parse.c | 186 +- src/p2p/p2p_pd.c | 858 +- src/p2p/p2p_sd.c | 328 +- src/p2p/p2p_utils.c | 422 +- src/pae/Makefile | 8 + src/pae/ieee802_1x_cp.c | 744 ++ src/pae/ieee802_1x_cp.h | 50 + src/pae/ieee802_1x_kay.c | 3541 +++++++++ src/pae/ieee802_1x_kay.h | 194 + src/pae/ieee802_1x_kay_i.h | 419 + src/pae/ieee802_1x_key.c | 189 + src/pae/ieee802_1x_key.h | 26 + src/pae/ieee802_1x_secy_ops.c | 492 ++ src/pae/ieee802_1x_secy_ops.h | 62 + src/radius/Makefile | 2 +- src/radius/radius.c | 225 +- src/radius/radius.h | 39 +- src/radius/radius_client.c | 371 +- src/radius/radius_das.c | 60 +- src/radius/radius_das.h | 12 +- src/radius/radius_server.c | 717 +- src/radius/radius_server.h | 31 + src/rsn_supp/Makefile | 2 +- src/rsn_supp/peerkey.c | 189 +- src/rsn_supp/peerkey.h | 13 +- src/rsn_supp/pmksa_cache.c | 79 +- src/rsn_supp/pmksa_cache.h | 16 +- src/rsn_supp/preauth.c | 54 +- src/rsn_supp/preauth.h | 6 +- src/rsn_supp/tdls.c | 1155 ++- src/rsn_supp/wpa.c | 842 +- src/rsn_supp/wpa.h | 83 +- src/rsn_supp/wpa_ft.c | 99 +- src/rsn_supp/wpa_i.h | 84 +- src/rsn_supp/wpa_ie.c | 168 +- src/rsn_supp/wpa_ie.h | 16 + src/tls/asn1.c | 33 +- src/tls/asn1.h | 6 +- src/tls/libtommath.c | 12 + src/tls/pkcs1.c | 165 +- src/tls/pkcs1.h | 7 + src/tls/rsa.c | 25 +- src/tls/rsa.h | 3 + src/tls/tlsv1_client.c | 52 +- src/tls/tlsv1_client_read.c | 124 +- src/tls/tlsv1_client_write.c | 38 +- src/tls/tlsv1_common.c | 178 +- src/tls/tlsv1_common.h | 13 +- src/tls/tlsv1_record.c | 2 +- src/tls/tlsv1_server.c | 226 +- src/tls/tlsv1_server.h | 5 + src/tls/tlsv1_server_i.h | 13 + src/tls/tlsv1_server_read.c | 438 +- src/tls/tlsv1_server_write.c | 193 +- src/tls/x509v3.c | 26 +- src/utils/Makefile | 4 +- src/utils/base64.c | 18 +- src/utils/bitfield.c | 89 + src/utils/bitfield.h | 21 + src/utils/browser-android.c | 128 + src/utils/browser-system.c | 119 + src/utils/browser-wpadebug.c | 136 + src/utils/browser.c | 219 + src/utils/browser.h | 21 + src/utils/build_config.h | 27 - src/utils/common.c | 496 +- src/utils/common.h | 213 +- src/utils/edit.c | 4 +- src/utils/edit_readline.c | 4 +- src/utils/edit_simple.c | 2 +- src/utils/eloop.c | 333 +- src/utils/eloop.h | 49 + src/utils/eloop_none.c | 395 - src/utils/eloop_win.c | 224 +- src/utils/ext_password_test.c | 2 +- src/utils/http-utils.h | 63 + src/utils/http_curl.c | 1641 ++++ src/utils/ip_addr.c | 24 - src/utils/ip_addr.h | 1 - src/utils/list.h | 2 + src/utils/os.h | 158 +- src/utils/os_internal.c | 89 +- src/utils/os_none.c | 17 + src/utils/os_unix.c | 232 +- src/utils/os_win32.c | 32 + src/utils/pcsc_funcs.c | 117 +- src/utils/pcsc_funcs.h | 11 +- src/utils/platform.h | 21 + src/utils/radiotap.c | 379 +- src/utils/radiotap.h | 56 +- src/utils/radiotap_iter.h | 108 +- src/utils/trace.c | 62 +- src/utils/trace.h | 1 + src/utils/utils_module_tests.c | 423 + src/utils/uuid.c | 2 +- src/utils/wpa_debug.c | 132 +- src/utils/wpa_debug.h | 68 +- src/utils/wpabuf.c | 9 + src/utils/wpabuf.h | 1 + src/utils/xml-utils.c | 471 ++ src/utils/xml-utils.h | 97 + src/utils/xml_libxml2.c | 457 ++ src/wps/Makefile | 2 +- src/wps/http_client.c | 2 +- src/wps/http_server.c | 10 + src/wps/httpread.c | 54 +- src/wps/ndef.c | 89 +- src/wps/wps.c | 78 +- src/wps/wps.h | 102 +- src/wps/wps_attr_build.c | 104 +- src/wps/wps_attr_parse.c | 43 +- src/wps/wps_attr_parse.h | 7 +- src/wps/wps_attr_process.c | 77 +- src/wps/wps_common.c | 360 +- src/wps/wps_defs.h | 69 +- src/wps/wps_dev_attr.c | 51 +- src/wps/wps_dev_attr.h | 6 +- src/wps/wps_enrollee.c | 234 +- src/wps/wps_er.c | 211 +- src/wps/wps_er.h | 1 + src/wps/wps_er_ssdp.c | 4 +- src/wps/wps_i.h | 23 +- src/wps/wps_module_tests.c | 337 + src/wps/wps_registrar.c | 346 +- src/wps/wps_upnp.c | 56 +- src/wps/wps_upnp_ap.c | 8 +- src/wps/wps_upnp_i.h | 3 +- src/wps/wps_upnp_ssdp.c | 32 +- src/wps/wps_upnp_web.c | 49 +- src/wps/wps_validate.c | 4 +- 403 files changed, 67872 insertions(+), 22909 deletions(-) create mode 100644 src/ap/acs.c create mode 100644 src/ap/acs.h create mode 100644 src/ap/bss_load.c create mode 100644 src/ap/bss_load.h create mode 100644 src/ap/dfs.c create mode 100644 src/ap/dfs.h create mode 100644 src/ap/dhcp_snoop.c create mode 100644 src/ap/dhcp_snoop.h create mode 100644 src/ap/ndisc_snoop.c create mode 100644 src/ap/ndisc_snoop.h create mode 100644 src/ap/x_snoop.c create mode 100644 src/ap/x_snoop.h create mode 100644 src/common/common_module_tests.c create mode 100644 src/common/hw_features_common.c create mode 100644 src/common/hw_features_common.h create mode 100644 src/common/ieee802_1x_defs.h create mode 100644 src/common/qca-vendor-attr.h create mode 100644 src/common/qca-vendor.h create mode 100644 src/common/sae.c create mode 100644 src/common/sae.h create mode 100644 src/common/tnc.h create mode 100644 src/common/wpa_helpers.c create mode 100644 src/common/wpa_helpers.h create mode 100644 src/crypto/aes-siv.c create mode 100644 src/crypto/aes_siv.h create mode 100644 src/crypto/crypto_module_tests.c delete mode 100644 src/crypto/crypto_nss.c delete mode 100644 src/crypto/fips_prf_cryptoapi.c delete mode 100644 src/crypto/fips_prf_gnutls.c delete mode 100644 src/crypto/fips_prf_nss.c create mode 100644 src/crypto/sha256-kdf.c create mode 100644 src/crypto/sha384.h delete mode 100644 src/crypto/tls_nss.c create mode 100644 src/drivers/driver_macsec_qca.c delete mode 100644 src/drivers/driver_madwifi.c create mode 100644 src/drivers/driver_nl80211.h create mode 100644 src/drivers/driver_nl80211_android.c create mode 100644 src/drivers/driver_nl80211_capa.c create mode 100644 src/drivers/driver_nl80211_event.c create mode 100644 src/drivers/driver_nl80211_monitor.c create mode 100644 src/drivers/driver_nl80211_scan.c create mode 100644 src/drivers/driver_openbsd.c delete mode 100644 src/drivers/driver_test.c create mode 100644 src/drivers/linux_defines.h create mode 100644 src/eap_common/eap_eke_common.c create mode 100644 src/eap_common/eap_eke_common.h create mode 100644 src/eap_peer/eap_eke.c create mode 100644 src/eap_peer/eap_proxy.h create mode 100644 src/eap_peer/eap_proxy_dummy.c create mode 100644 src/eap_server/eap_server_eke.c create mode 100644 src/pae/Makefile create mode 100644 src/pae/ieee802_1x_cp.c create mode 100644 src/pae/ieee802_1x_cp.h create mode 100644 src/pae/ieee802_1x_kay.c create mode 100644 src/pae/ieee802_1x_kay.h create mode 100644 src/pae/ieee802_1x_kay_i.h create mode 100644 src/pae/ieee802_1x_key.c create mode 100644 src/pae/ieee802_1x_key.h create mode 100644 src/pae/ieee802_1x_secy_ops.c create mode 100644 src/pae/ieee802_1x_secy_ops.h create mode 100644 src/utils/bitfield.c create mode 100644 src/utils/bitfield.h create mode 100644 src/utils/browser-android.c create mode 100644 src/utils/browser-system.c create mode 100644 src/utils/browser-wpadebug.c create mode 100644 src/utils/browser.c create mode 100644 src/utils/browser.h delete mode 100644 src/utils/eloop_none.c create mode 100644 src/utils/http-utils.h create mode 100644 src/utils/http_curl.c create mode 100644 src/utils/platform.h create mode 100644 src/utils/utils_module_tests.c create mode 100644 src/utils/xml-utils.c create mode 100644 src/utils/xml-utils.h create mode 100644 src/utils/xml_libxml2.c create mode 100644 src/wps/wps_module_tests.c (limited to 'src') diff --git a/src/Makefile b/src/Makefile index d73a175abc86..10e0171b0c6d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,4 @@ -SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps +SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps all: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done diff --git a/src/ap/Makefile b/src/ap/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/ap/Makefile +++ b/src/ap/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 954053131a7a..7c55146b2c5c 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -10,7 +10,8 @@ #include "utils/common.h" #include "utils/eloop.h" -#include "drivers/driver.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" @@ -44,19 +45,26 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); return NULL; } if (sta) { radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Acct-Session-Id\n"); - goto fail; + if ((hapd->conf->wpa & 2) && + !hapd->conf->disable_pmksa_caching && + sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) { + os_snprintf(buf, sizeof(buf), "%08X+%08X", + sta->eapol_sm->acct_multi_session_id_hi, + sta->eapol_sm->acct_multi_session_id_lo); + if (!radius_msg_add_attr( + msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_INFO, + "Could not add Acct-Multi-Session-Id"); + goto fail; + } } } else { radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); @@ -64,7 +72,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { - printf("Could not add Acct-Status-Type\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); goto fail; } @@ -74,7 +82,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, hapd->conf->ieee802_1x ? RADIUS_ACCT_AUTHENTIC_RADIUS : RADIUS_ACCT_AUTHENTIC_LOCAL)) { - printf("Could not add Acct-Authentic\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); goto fail; } @@ -99,7 +107,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, len)) { - printf("Could not add User-Name\n"); + wpa_printf(MSG_INFO, "Could not add User-Name"); goto fail; } } @@ -117,7 +125,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, val, len)) { - printf("Could not add Class\n"); + wpa_printf(MSG_INFO, "Could not add Class"); goto fail; } } @@ -202,7 +210,6 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; - struct os_time t; int interval; if (sta->acct_session_started) @@ -213,8 +220,7 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) "starting accounting session %08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); - os_get_time(&t); - sta->acct_session_start = t.sec; + os_get_reltime(&sta->acct_session_start); sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->acct_input_gigawords = sta->acct_output_gigawords = 0; hostapd_drv_sta_clear_stats(hapd, sta->addr); @@ -244,6 +250,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, struct radius_msg *msg; int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; + struct os_reltime now_r, diff; struct os_time now; u32 gigawords; @@ -254,14 +261,16 @@ static void accounting_sta_report(struct hostapd_data *hapd, stop ? RADIUS_ACCT_STATUS_TYPE_STOP : RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); if (!msg) { - printf("Could not create RADIUS Accounting message\n"); + wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); return; } + os_get_reltime(&now_r); os_get_time(&now); + os_reltime_sub(&now_r, &sta->acct_session_start, &diff); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, - now.sec - sta->acct_session_start)) { - printf("Could not add Acct-Session-Time\n"); + diff.sec)) { + wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); goto fail; } @@ -269,19 +278,19 @@ static void accounting_sta_report(struct hostapd_data *hapd, if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS, data.rx_packets)) { - printf("Could not add Acct-Input-Packets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_PACKETS, data.tx_packets)) { - printf("Could not add Acct-Output-Packets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, data.rx_bytes)) { - printf("Could not add Acct-Input-Octets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } gigawords = sta->acct_input_gigawords; @@ -292,13 +301,13 @@ static void accounting_sta_report(struct hostapd_data *hapd, !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, gigawords)) { - printf("Could not add Acct-Input-Gigawords\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); goto fail; } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, data.tx_bytes)) { - printf("Could not add Acct-Output-Octets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } gigawords = sta->acct_output_gigawords; @@ -309,14 +318,14 @@ static void accounting_sta_report(struct hostapd_data *hapd, !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, gigawords)) { - printf("Could not add Acct-Output-Gigawords\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); goto fail; } } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, now.sec)) { - printf("Could not add Event-Timestamp\n"); + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); goto fail; } @@ -326,7 +335,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, if (stop && cause && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, cause)) { - printf("Could not add Acct-Terminate-Cause\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); goto fail; } @@ -400,13 +409,12 @@ accounting_receive(struct radius_msg *msg, struct radius_msg *req, void *data) { if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { - printf("Unknown RADIUS message code\n"); + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { - printf("Incoming RADIUS packet did not have correct " - "Authenticator - dropped\n"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } @@ -432,7 +440,7 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) { - printf("Could not add Acct-Terminate-Cause\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); radius_msg_free(msg); return; } diff --git a/src/ap/acs.c b/src/ap/acs.c new file mode 100644 index 000000000000..ae7f6c309289 --- /dev/null +++ b/src/ap/acs.c @@ -0,0 +1,949 @@ +/* + * ACS - Automatic Channel Selection module + * Copyright (c) 2011, Atheros Communications + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include + +#include "utils/common.h" +#include "utils/list.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#include "hw_features.h" +#include "acs.h" + +/* + * Automatic Channel Selection + * =========================== + * + * More info at + * ------------ + * http://wireless.kernel.org/en/users/Documentation/acs + * + * How to use + * ---------- + * - make sure you have CONFIG_ACS=y in hostapd's .config + * - use channel=0 or channel=acs to enable ACS + * + * How does it work + * ---------------- + * 1. passive scans are used to collect survey data + * (it is assumed that scan trigger collection of survey data in driver) + * 2. interference factor is calculated for each channel + * 3. ideal channel is picked depending on channel width by using adjacent + * channel interference factors + * + * Known limitations + * ----------------- + * - Current implementation depends heavily on the amount of time willing to + * spend gathering survey data during hostapd startup. Short traffic bursts + * may be missed and a suboptimal channel may be picked. + * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS + * + * Todo / Ideas + * ------------ + * - implement other interference computation methods + * - BSS/RSSI based + * - spectral scan based + * (should be possibly to hook this up with current ACS scans) + * - add wpa_supplicant support (for P2P) + * - collect a histogram of interference over time allowing more educated + * guess about an ideal channel (perhaps CSA could be used to migrate AP to a + * new "better" channel while running) + * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs + * when choosing the ideal channel + * + * Survey interference factor implementation details + * ------------------------------------------------- + * Generic interference_factor in struct hostapd_channel_data is used. + * + * The survey interference factor is defined as the ratio of the + * observed busy time over the time we spent on the channel, + * this value is then amplified by the observed noise floor on + * the channel in comparison to the lowest noise floor observed + * on the entire band. + * + * This corresponds to: + * --- + * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf) + * --- + * + * The coefficient of 2 reflects the way power in "far-field" + * radiation decreases as the square of distance from the antenna [1]. + * What this does is it decreases the observed busy time ratio if the + * noise observed was low but increases it if the noise was high, + * proportionally to the way "far field" radiation changes over + * distance. + * + * If channel busy time is not available the fallback is to use channel RX time. + * + * Since noise floor is in dBm it is necessary to convert it into Watts so that + * combined channel interference (e.g., HT40, which uses two channels) can be + * calculated easily. + * --- + * (busy time - tx time) / (active time - tx time) * + * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) + * --- + * + * However to account for cases where busy/rx time is 0 (channel load is then + * 0%) channel noise floor signal power is combined into the equation so a + * channel with lower noise floor is preferred. The equation becomes: + * --- + * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) * + * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) + * --- + * + * All this "interference factor" is purely subjective and only time + * will tell how usable this is. By using the minimum noise floor we + * remove any possible issues due to card calibration. The computation + * of the interference factor then is dependent on what the card itself + * picks up as the minimum noise, not an actual real possible card + * noise value. + * + * Total interference computation details + * -------------------------------------- + * The above channel interference factor is calculated with no respect to + * target operational bandwidth. + * + * To find an ideal channel the above data is combined by taking into account + * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels + * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth + * on 5 GHz. + * + * Each valid and possible channel spec (i.e., channel + width) is taken and its + * interference factor is computed by summing up interferences of each channel + * it overlaps. The one with least total interference is picked up. + * + * Note: This implies base channel interference factor must be non-negative + * allowing easy summing up. + * + * Example ACS analysis printout + * ----------------------------- + * + * ACS: Trying survey-based ACS + * ACS: Survey analysis for channel 1 (2412 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13 + * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 + * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11 + * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 + * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 + * ACS: * interference factor average: 0.0557166 + * ACS: Survey analysis for channel 2 (2417 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 + * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4 + * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6 + * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24 + * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 + * ACS: * interference factor average: 0.050832 + * ACS: Survey analysis for channel 3 (2422 MHz) + * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 + * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: * interference factor average: 0.0148838 + * ACS: Survey analysis for channel 4 (2427 MHz) + * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 + * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: * interference factor average: 0.0160801 + * ACS: Survey analysis for channel 5 (2432 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66 + * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7 + * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2 + * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109 + * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 + * ACS: * interference factor average: 0.232244 + * ACS: Survey analysis for channel 6 (2437 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89 + * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13 + * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 + * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70 + * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 + * ACS: * interference factor average: 0.232298 + * ACS: Survey analysis for channel 7 (2442 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71 + * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62 + * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 + * ACS: * interference factor average: 0.195031 + * ACS: Survey analysis for channel 8 (2447 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8 + * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8 + * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21 + * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27 + * ACS: * interference factor average: 0.0865885 + * ACS: Survey analysis for channel 9 (2452 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2 + * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.00993022 + * ACS: Survey analysis for channel 10 (2457 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.0136033 + * ACS: Survey analysis for channel 11 (2462 MHz) + * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 + * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7 + * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15 + * ACS: * interference factor average: 0.0271605 + * ACS: Survey analysis for channel 12 (2467 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 + * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1 + * ACS: * interference factor average: 0.0148992 + * ACS: Survey analysis for channel 13 (2472 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12 + * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.0260179 + * ACS: Survey analysis for selected bandwidth 20MHz + * ACS: * channel 1: total interference = 0.121432 + * ACS: * channel 2: total interference = 0.137512 + * ACS: * channel 3: total interference = 0.369757 + * ACS: * channel 4: total interference = 0.546338 + * ACS: * channel 5: total interference = 0.690538 + * ACS: * channel 6: total interference = 0.762242 + * ACS: * channel 7: total interference = 0.756092 + * ACS: * channel 8: total interference = 0.537451 + * ACS: * channel 9: total interference = 0.332313 + * ACS: * channel 10: total interference = 0.152182 + * ACS: * channel 11: total interference = 0.0916111 + * ACS: * channel 12: total interference = 0.0816809 + * ACS: * channel 13: total interference = 0.0680776 + * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776 + * + * [1] http://en.wikipedia.org/wiki/Near_and_far_field + */ + + +static int acs_request_scan(struct hostapd_iface *iface); +static int acs_survey_is_sufficient(struct freq_survey *survey); + + +static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) +{ + struct freq_survey *survey, *tmp; + + if (dl_list_empty(&chan->survey_list)) + return; + + dl_list_for_each_safe(survey, tmp, &chan->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } +} + + +static void acs_cleanup(struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) + acs_clean_chan_surveys(chan); + + dl_list_init(&chan->survey_list); + chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; + chan->min_nf = 0; + } + + iface->chans_surveyed = 0; + iface->acs_num_completed_scans = 0; +} + + +static void acs_fail(struct hostapd_iface *iface) +{ + wpa_printf(MSG_ERROR, "ACS: Failed to start"); + acs_cleanup(iface); + hostapd_disable_iface(iface); +} + + +static long double +acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) +{ + long double factor, busy, total; + + if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) + busy = survey->channel_time_busy; + else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX) + busy = survey->channel_time_rx; + else { + /* This shouldn't really happen as survey data is checked in + * acs_sanity_check() */ + wpa_printf(MSG_ERROR, "ACS: Survey data missing"); + return 0; + } + + total = survey->channel_time; + + if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) { + busy -= survey->channel_time_tx; + total -= survey->channel_time_tx; + } + + /* TODO: figure out the best multiplier for noise floor base */ + factor = pow(10, survey->nf / 5.0L) + + (busy / total) * + pow(2, pow(10, (long double) survey->nf / 10.0L) - + pow(10, (long double) min_nf / 10.0L)); + + return factor; +} + + +static void +acs_survey_chan_interference_factor(struct hostapd_iface *iface, + struct hostapd_channel_data *chan) +{ + struct freq_survey *survey; + unsigned int i = 0; + long double int_factor = 0; + unsigned count = 0; + + if (dl_list_empty(&chan->survey_list)) + return; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return; + + chan->interference_factor = 0; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) + { + i++; + + if (!acs_survey_is_sufficient(survey)) { + wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i); + continue; + } + + count++; + int_factor = acs_survey_interference_factor(survey, + iface->lowest_nf); + chan->interference_factor += int_factor; + wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu", + i, chan->min_nf, int_factor, + survey->nf, (unsigned long) survey->channel_time, + (unsigned long) survey->channel_time_busy, + (unsigned long) survey->channel_time_rx); + } + + if (!count) + return; + chan->interference_factor /= count; +} + + +static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, + 157, 184, 192 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(allowed); i++) + if (chan->chan == allowed[i]) + return 1; + + return 0; +} + + +static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 52, 100, 116, 132, 149 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(allowed); i++) + if (chan->chan == allowed[i]) + return 1; + + return 0; +} + + +static int acs_survey_is_sufficient(struct freq_survey *survey) +{ + if (!(survey->filled & SURVEY_HAS_NF)) { + wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor"); + return 0; + } + + if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) { + wpa_printf(MSG_INFO, "ACS: Survey is missing channel time"); + return 0; + } + + if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) && + !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) { + wpa_printf(MSG_INFO, + "ACS: Survey is missing RX and busy time (at least one is required)"); + return 0; + } + + return 1; +} + + +static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan) +{ + struct freq_survey *survey; + int ret = -1; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) + { + if (acs_survey_is_sufficient(survey)) { + ret = 1; + break; + } + ret = 0; + } + + if (ret == -1) + ret = 1; /* no survey list entries */ + + if (!ret) { + wpa_printf(MSG_INFO, + "ACS: Channel %d has insufficient survey data", + chan->chan); + } + + return ret; +} + + +static int acs_surveys_are_sufficient(struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + int valid = 0; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (!acs_survey_list_is_sufficient(chan)) + continue; + + valid++; + } + + /* We need at least survey data for one channel */ + return !!valid; +} + + +static int acs_usable_chan(struct hostapd_channel_data *chan) +{ + if (dl_list_empty(&chan->survey_list)) + return 0; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return 0; + if (!acs_survey_list_is_sufficient(chan)) + return 0; + return 1; +} + + +static int is_in_chanlist(struct hostapd_iface *iface, + struct hostapd_channel_data *chan) +{ + int *entry; + + if (!iface->conf->chanlist) + return 1; + + for (entry = iface->conf->chanlist; *entry != -1; entry++) { + if (*entry == chan->chan) + return 1; + } + return 0; +} + + +static void acs_survey_all_chans_intereference_factor( + struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (!acs_usable_chan(chan)) + continue; + + if (!is_in_chanlist(iface, chan)) + continue; + + wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", + chan->chan, chan->freq); + + acs_survey_chan_interference_factor(iface, chan); + + wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg", + chan->interference_factor); + } +} + + +static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, + int freq) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (chan->freq == freq) + return chan; + } + + return NULL; +} + + +static int is_24ghz_mode(enum hostapd_hw_mode mode) +{ + return mode == HOSTAPD_MODE_IEEE80211B || + mode == HOSTAPD_MODE_IEEE80211G; +} + + +static int is_common_24ghz_chan(int chan) +{ + return chan == 1 || chan == 6 || chan == 11; +} + + +#ifndef ACS_ADJ_WEIGHT +#define ACS_ADJ_WEIGHT 0.85 +#endif /* ACS_ADJ_WEIGHT */ + +#ifndef ACS_NEXT_ADJ_WEIGHT +#define ACS_NEXT_ADJ_WEIGHT 0.55 +#endif /* ACS_NEXT_ADJ_WEIGHT */ + +#ifndef ACS_24GHZ_PREFER_1_6_11 +/* + * Select commonly used channels 1, 6, 11 by default even if a neighboring + * channel has a smaller interference factor as long as it is not better by more + * than this multiplier. + */ +#define ACS_24GHZ_PREFER_1_6_11 0.8 +#endif /* ACS_24GHZ_PREFER_1_6_11 */ + +/* + * At this point it's assumed chan->interface_factor has been computed. + * This function should be reusable regardless of interference computation + * option (survey, BSS, spectral, ...). chan->interference factor must be + * summable (i.e., must be always greater than zero). + */ +static struct hostapd_channel_data * +acs_find_ideal_chan(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL, + *rand_chan = NULL; + long double factor, ideal_factor = 0; + int i, j; + int n_chans = 1; + unsigned int k; + + /* TODO: HT40- support */ + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel == -1) { + wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); + return NULL; + } + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac && + iface->conf->vht_oper_chwidth == 1) + n_chans = 4; + + /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ + + wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", + n_chans == 1 ? 20 : + n_chans == 2 ? 40 : + n_chans == 4 ? 80 : + -1); + + for (i = 0; i < iface->current_mode->num_channels; i++) { + double total_weight; + struct acs_bias *bias, tmp_bias; + + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (!is_in_chanlist(iface, chan)) + continue; + + /* HT40 on 5 GHz has a limited set of primary channels as per + * 11n Annex J */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + iface->conf->ieee80211n && + iface->conf->secondary_channel && + !acs_usable_ht40_chan(chan)) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40", + chan->chan); + continue; + } + + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + iface->conf->ieee80211ac && + iface->conf->vht_oper_chwidth == 1 && + !acs_usable_vht80_chan(chan)) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80", + chan->chan); + continue; + } + + factor = 0; + if (acs_usable_chan(chan)) + factor = chan->interference_factor; + total_weight = 1; + + for (j = 1; j < n_chans; j++) { + adj_chan = acs_find_chan(iface, chan->freq + (j * 20)); + if (!adj_chan) + break; + + if (acs_usable_chan(adj_chan)) { + factor += adj_chan->interference_factor; + total_weight += 1; + } + } + + if (j != n_chans) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth", + chan->chan); + continue; + } + + /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent + * channel interference factor. */ + if (is_24ghz_mode(iface->current_mode->mode)) { + for (j = 0; j < n_chans; j++) { + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) - 5); + if (adj_chan && acs_usable_chan(adj_chan)) { + factor += ACS_ADJ_WEIGHT * + adj_chan->interference_factor; + total_weight += ACS_ADJ_WEIGHT; + } + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) - 10); + if (adj_chan && acs_usable_chan(adj_chan)) { + factor += ACS_NEXT_ADJ_WEIGHT * + adj_chan->interference_factor; + total_weight += ACS_NEXT_ADJ_WEIGHT; + } + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) + 5); + if (adj_chan && acs_usable_chan(adj_chan)) { + factor += ACS_ADJ_WEIGHT * + adj_chan->interference_factor; + total_weight += ACS_ADJ_WEIGHT; + } + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) + 10); + if (adj_chan && acs_usable_chan(adj_chan)) { + factor += ACS_NEXT_ADJ_WEIGHT * + adj_chan->interference_factor; + total_weight += ACS_NEXT_ADJ_WEIGHT; + } + } + } + + factor /= total_weight; + + bias = NULL; + if (iface->conf->acs_chan_bias) { + for (k = 0; k < iface->conf->num_acs_chan_bias; k++) { + bias = &iface->conf->acs_chan_bias[k]; + if (bias->channel == chan->chan) + break; + bias = NULL; + } + } else if (is_24ghz_mode(iface->current_mode->mode) && + is_common_24ghz_chan(chan->chan)) { + tmp_bias.channel = chan->chan; + tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11; + bias = &tmp_bias; + } + + if (bias) { + factor *= bias->bias; + wpa_printf(MSG_DEBUG, + "ACS: * channel %d: total interference = %Lg (%f bias)", + chan->chan, factor, bias->bias); + } else { + wpa_printf(MSG_DEBUG, + "ACS: * channel %d: total interference = %Lg", + chan->chan, factor); + } + + if (acs_usable_chan(chan) && + (!ideal_chan || factor < ideal_factor)) { + ideal_factor = factor; + ideal_chan = chan; + } + + /* This channel would at least be usable */ + if (!rand_chan) + rand_chan = chan; + } + + if (ideal_chan) { + wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg", + ideal_chan->chan, ideal_chan->freq, ideal_factor); + return ideal_chan; + } + + return rand_chan; +} + + +static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) +{ + int offset; + + wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency"); + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + offset = 2 * iface->conf->secondary_channel; + break; + case VHT_CHANWIDTH_80MHZ: + offset = 6; + break; + default: + /* TODO: How can this be calculated? Adjust + * acs_find_ideal_chan() */ + wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); + return; + } + + iface->conf->vht_oper_centr_freq_seg0_idx = + iface->conf->channel + offset; +} + + +static int acs_study_survey_based(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS"); + + if (!iface->chans_surveyed) { + wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data"); + return -1; + } + + if (!acs_surveys_are_sufficient(iface)) { + wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data"); + return -1; + } + + acs_survey_all_chans_intereference_factor(iface); + return 0; +} + + +static int acs_study_options(struct hostapd_iface *iface) +{ + int err; + + err = acs_study_survey_based(iface); + if (err == 0) + return 0; + + /* TODO: If no surveys are available/sufficient this is a good + * place to fallback to BSS-based ACS */ + + return -1; +} + + +static void acs_study(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *ideal_chan; + int err; + + err = acs_study_options(iface); + if (err < 0) { + wpa_printf(MSG_ERROR, "ACS: All study options have failed"); + goto fail; + } + + ideal_chan = acs_find_ideal_chan(iface); + if (!ideal_chan) { + wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel"); + err = -1; + goto fail; + } + + iface->conf->channel = ideal_chan->chan; + + if (iface->conf->ieee80211ac) + acs_adjust_vht_center_freq(iface); + + err = 0; +fail: + /* + * hostapd_setup_interface_complete() will return -1 on failure, + * 0 on success and 0 is HOSTAPD_CHAN_VALID :) + */ + if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) { + acs_cleanup(iface); + return; + } + + /* This can possibly happen if channel parameters (secondary + * channel, center frequencies) are misconfigured */ + wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file."); + acs_fail(iface); +} + + +static void acs_scan_complete(struct hostapd_iface *iface) +{ + int err; + + iface->scan_cb = NULL; + + wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)", + iface->conf->acs_num_scans); + + err = hostapd_drv_get_survey(iface->bss[0], 0); + if (err) { + wpa_printf(MSG_ERROR, "ACS: Failed to get survey data"); + goto fail; + } + + if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) { + err = acs_request_scan(iface); + if (err) { + wpa_printf(MSG_ERROR, "ACS: Failed to request scan"); + goto fail; + } + + return; + } + + acs_study(iface); + return; +fail: + hostapd_acs_completed(iface, 1); + acs_fail(iface); +} + + +static int acs_request_scan(struct hostapd_iface *iface) +{ + struct wpa_driver_scan_params params; + struct hostapd_channel_data *chan; + int i, *freq; + + os_memset(¶ms, 0, sizeof(params)); + params.freqs = os_calloc(iface->current_mode->num_channels + 1, + sizeof(params.freqs[0])); + if (params.freqs == NULL) + return -1; + + freq = params.freqs; + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + *freq++ = chan->freq; + } + *freq = 0; + + iface->scan_cb = acs_scan_complete; + + wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", + iface->acs_num_completed_scans + 1, + iface->conf->acs_num_scans); + + if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { + wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan"); + acs_cleanup(iface); + os_free(params.freqs); + return -1; + } + + os_free(params.freqs); + return 0; +} + + +enum hostapd_chan_status acs_init(struct hostapd_iface *iface) +{ + int err; + + wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); + + if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { + wpa_printf(MSG_INFO, "ACS: Offloading to driver"); + err = hostapd_drv_do_acs(iface->bss[0]); + if (err) + return HOSTAPD_CHAN_INVALID; + return HOSTAPD_CHAN_ACS; + } + + acs_cleanup(iface); + + err = acs_request_scan(iface); + if (err < 0) + return HOSTAPD_CHAN_INVALID; + + hostapd_set_state(iface, HAPD_IFACE_ACS); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED); + + return HOSTAPD_CHAN_ACS; +} diff --git a/src/ap/acs.h b/src/ap/acs.h new file mode 100644 index 000000000000..fc85259e85d5 --- /dev/null +++ b/src/ap/acs.h @@ -0,0 +1,27 @@ +/* + * ACS - Automatic Channel Selection module + * Copyright (c) 2011, Atheros Communications + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ACS_H +#define ACS_H + +#ifdef CONFIG_ACS + +enum hostapd_chan_status acs_init(struct hostapd_iface *iface); + +#else /* CONFIG_ACS */ + +static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) +{ + wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel"); + return HOSTAPD_CHAN_INVALID; +} + +#endif /* CONFIG_ACS */ + +#endif /* ACS_H */ diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 25d26e5e77a7..76011dc07fd4 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration helper functions - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -73,6 +73,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #ifdef CONFIG_IEEE80211W bss->assoc_sa_query_max_timeout = 1000; bss->assoc_sa_query_retry_timeout = 201; + bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC; #endif /* CONFIG_IEEE80211W */ #ifdef EAP_SERVER_FAST /* both anonymous and authenticated provisioning */ @@ -89,6 +90,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) #endif /* CONFIG_IEEE80211R */ bss->radius_das_time_window = 300; + + bss->sae_anti_clogging_threshold = 5; } @@ -104,9 +107,9 @@ struct hostapd_config * hostapd_config_defaults(void) 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 }; + { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 }; const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ - { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; + { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 }; const struct hostapd_tx_queue_params txq_bk = { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; const struct hostapd_tx_queue_params txq_be = @@ -128,9 +131,17 @@ struct hostapd_config * hostapd_config_defaults(void) os_free(bss); return NULL; } + conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *)); + if (conf->bss == NULL) { + os_free(conf); + os_free(bss); + return NULL; + } + conf->bss[0] = bss; bss->radius = os_zalloc(sizeof(*bss->radius)); if (bss->radius == NULL) { + os_free(conf->bss); os_free(conf); os_free(bss); return NULL; @@ -139,12 +150,13 @@ struct hostapd_config * hostapd_config_defaults(void) hostapd_config_defaults_bss(bss); conf->num_bss = 1; - conf->bss = bss; conf->beacon_int = 100; conf->rts_threshold = -1; /* use driver default: 2347 */ conf->fragm_threshold = -1; /* user driver default: 2346 */ conf->send_probe_response = 1; + /* Set to invalid value means do not add Power Constraint IE */ + conf->local_pwr_constraint = -1; conf->wmm_ac_params[0] = ac_be; conf->wmm_ac_params[1] = ac_bk; @@ -161,6 +173,18 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ap_table_max_size = 255; conf->ap_table_expiration_time = 60; +#ifdef CONFIG_TESTING_OPTIONS + conf->ignore_probe_probability = 0.0; + conf->ignore_auth_probability = 0.0; + conf->ignore_assoc_probability = 0.0; + conf->ignore_reassoc_probability = 0.0; + conf->corrupt_gtk_rekey_mic_probability = 0.0; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_ACS + conf->acs_num_scans = 5; +#endif /* CONFIG_ACS */ + return conf; } @@ -315,20 +339,6 @@ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) } -int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) -{ - int i; - - if (a->idx != b->idx || a->default_len != b->default_len) - return 1; - for (i = 0; i < NUM_WEP_KEYS; i++) - if (a->len[i] != b->len[i] || - os_memcmp(a->key[i], b->key[i], a->len[i]) != 0) - return 1; - return 0; -} - - static void hostapd_config_free_radius(struct hostapd_radius_server *servers, int num_servers) { @@ -365,10 +375,11 @@ static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) } -static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) +void hostapd_config_free_eap_user(struct hostapd_eap_user *user) { + hostapd_config_free_radius_attr(user->accept_attr); os_free(user->identity); - os_free(user->password); + bin_clear_free(user->password, user->password_len); os_free(user); } @@ -377,28 +388,35 @@ static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) { int i; for (i = 0; i < NUM_WEP_KEYS; i++) { - os_free(keys->key[i]); + bin_clear_free(keys->key[i], keys->len[i]); keys->key[i] = NULL; } } -static void hostapd_config_free_bss(struct hostapd_bss_config *conf) +void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l) +{ + struct hostapd_wpa_psk *psk, *tmp; + + for (psk = *l; psk;) { + tmp = psk; + psk = psk->next; + bin_clear_free(tmp, sizeof(*tmp)); + } + *l = NULL; +} + + +void hostapd_config_free_bss(struct hostapd_bss_config *conf) { - struct hostapd_wpa_psk *psk, *prev; struct hostapd_eap_user *user, *prev_user; if (conf == NULL) return; - psk = conf->ssid.wpa_psk; - while (psk) { - prev = psk; - psk = psk->next; - os_free(prev); - } + hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk); - os_free(conf->ssid.wpa_passphrase); + str_clear_free(conf->ssid.wpa_passphrase); os_free(conf->ssid.wpa_psk_file); hostapd_config_free_wep(&conf->ssid.wep); #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -413,15 +431,17 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) } os_free(conf->eap_user_sqlite); - os_free(conf->dump_log_name); os_free(conf->eap_req_id_text); + os_free(conf->erp_domain); os_free(conf->accept_mac); os_free(conf->deny_mac); os_free(conf->nas_identifier); - hostapd_config_free_radius(conf->radius->auth_servers, - conf->radius->num_auth_servers); - hostapd_config_free_radius(conf->radius->acct_servers, - conf->radius->num_acct_servers); + if (conf->radius) { + hostapd_config_free_radius(conf->radius->auth_servers, + conf->radius->num_auth_servers); + hostapd_config_free_radius(conf->radius->acct_servers, + conf->radius->num_acct_servers); + } hostapd_config_free_radius_attr(conf->radius_auth_req_attr); hostapd_config_free_radius_attr(conf->radius_acct_req_attr); os_free(conf->rsn_preauth_interfaces); @@ -430,29 +450,17 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->server_cert); os_free(conf->private_key); os_free(conf->private_key_passwd); + os_free(conf->ocsp_stapling_response); os_free(conf->dh_file); + os_free(conf->openssl_ciphers); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); os_free(conf->eap_sim_db); os_free(conf->radius_server_clients); - os_free(conf->test_socket); os_free(conf->radius); os_free(conf->radius_das_shared_secret); hostapd_config_free_vlan(conf); - if (conf->ssid.dyn_vlan_keys) { - struct hostapd_ssid *ssid = &conf->ssid; - size_t i; - for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { - if (ssid->dyn_vlan_keys[i] == NULL) - continue; - hostapd_config_free_wep(ssid->dyn_vlan_keys[i]); - os_free(ssid->dyn_vlan_keys[i]); - } - os_free(ssid->dyn_vlan_keys); - ssid->dyn_vlan_keys = NULL; - } - os_free(conf->time_zone); #ifdef CONFIG_IEEE80211R @@ -495,6 +503,12 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_description); os_free(conf->model_url); os_free(conf->upc); + { + unsigned int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(conf->wps_vendor_ext[i]); + } wpabuf_free(conf->wps_nfc_dh_pubkey); wpabuf_free(conf->wps_nfc_dh_privkey); wpabuf_free(conf->wps_nfc_dev_pw); @@ -516,9 +530,36 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->hs20_wan_metrics); os_free(conf->hs20_connection_capability); os_free(conf->hs20_operating_class); + os_free(conf->hs20_icons); + if (conf->hs20_osu_providers) { + size_t i; + for (i = 0; i < conf->hs20_osu_providers_count; i++) { + struct hs20_osu_provider *p; + size_t j; + p = &conf->hs20_osu_providers[i]; + os_free(p->friendly_name); + os_free(p->server_uri); + os_free(p->method_list); + for (j = 0; j < p->icons_count; j++) + os_free(p->icons[j]); + os_free(p->icons); + os_free(p->osu_nai); + os_free(p->service_desc); + } + os_free(conf->hs20_osu_providers); + } + os_free(conf->subscr_remediation_url); #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); + + os_free(conf->sae_groups); + + os_free(conf->wowlan_triggers); + + os_free(conf->server_id); + + os_free(conf); } @@ -534,10 +575,15 @@ void hostapd_config_free(struct hostapd_config *conf) return; for (i = 0; i < conf->num_bss; i++) - hostapd_config_free_bss(&conf->bss[i]); + hostapd_config_free_bss(conf->bss[i]); os_free(conf->bss); os_free(conf->supported_rates); os_free(conf->basic_rates); + os_free(conf->chanlist); + os_free(conf->driver_params); +#ifdef CONFIG_ACS + os_free(conf->acs_chan_bias); +#endif /* CONFIG_ACS */ os_free(conf); } @@ -594,11 +640,23 @@ int hostapd_rate_found(int *list, int rate) } -const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) { struct hostapd_vlan *v = vlan; while (v) { if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + return 1; + v = v->next; + } + return 0; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + struct hostapd_vlan *v = vlan; + while (v) { + if (v->vlan_id == vlan_id) return v->ifname; v = v->next; } @@ -607,14 +665,30 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, - const u8 *addr, const u8 *prev_psk) + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk) { struct hostapd_wpa_psk *psk; int next_ok = prev_psk == NULL; + if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " p2p_dev_addr=" MACSTR " prev_psk=%p", + MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk); + addr = NULL; /* Use P2P Device Address for matching */ + } else { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " prev_psk=%p", + MAC2STR(addr), prev_psk); + } + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { if (next_ok && - (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0)) + (psk->group || + (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) || + (!addr && p2p_dev_addr && + os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == + 0))) return psk->psk; if (psk->psk == prev_psk) @@ -623,3 +697,250 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, return NULL; } + + +static int hostapd_config_check_bss(struct hostapd_bss_config *bss, + struct hostapd_config *conf, + int full_config) +{ + if (full_config && bss->ieee802_1x && !bss->eap_server && + !bss->radius->auth_servers) { + wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " + "EAP authenticator configured)."); + return -1; + } + + if (bss->wpa) { + int wep, i; + + wep = bss->default_wep_key_len > 0 || + bss->individual_wep_key_len > 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss->ssid.wep.keys_set) { + wep = 1; + break; + } + } + + if (wep) { + wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported"); + return -1; + } + } + + if (full_config && bss->wpa && + bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no " + "RADIUS checking (macaddr_acl=2) enabled."); + return -1; + } + + if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && + bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && + bss->ssid.wpa_psk_file == NULL && + (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED || + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) { + wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " + "is not configured."); + return -1; + } + + if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (conf->bss[i] != bss && + (hostapd_mac_comp(conf->bss[i]->bssid, + bss->bssid) == 0)) { + wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR + " on interface '%s' and '%s'.", + MAC2STR(bss->bssid), + conf->bss[i]->iface, bss->iface); + return -1; + } + } + } + +#ifdef CONFIG_IEEE80211R + if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && + (bss->nas_identifier == NULL || + os_strlen(bss->nas_identifier) < 1 || + os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { + wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " + "nas_identifier to be configured as a 1..48 octet " + "string"); + return -1; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211N + if (full_config && conf->ieee80211n && + conf->hw_mode == HOSTAPD_MODE_IEEE80211B) { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not " + "allowed, disabling HT capabilities"); + } + + if (full_config && conf->ieee80211n && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not " + "allowed, disabling HT capabilities"); + } + + if (full_config && conf->ieee80211n && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " + "requires CCMP/GCMP to be enabled, disabling HT " + "capabilities"); + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_WPS + if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { + wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " + "configuration forced WPS to be disabled"); + bss->wps_state = 0; + } + + if (full_config && bss->wps_state && + bss->ssid.wep.keys_set && bss->wpa == 0) { + wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be " + "disabled"); + bss->wps_state = 0; + } + + if (full_config && bss->wps_state && bss->wpa && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { + wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " + "WPA2/CCMP forced WPS to be disabled"); + bss->wps_state = 0; + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_HS20 + if (full_config && bss->hs20 && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)))) { + wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP " + "configuration is required for Hotspot 2.0 " + "functionality"); + return -1; + } +#endif /* CONFIG_HS20 */ + + return 0; +} + + +int hostapd_config_check(struct hostapd_config *conf, int full_config) +{ + size_t i; + + if (full_config && conf->ieee80211d && + (!conf->country[0] || !conf->country[1])) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " + "setting the country_code"); + return -1; + } + + if (full_config && conf->ieee80211h && !conf->ieee80211d) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without " + "IEEE 802.11d enabled"); + return -1; + } + + if (full_config && conf->local_pwr_constraint != -1 && + !conf->ieee80211d) { + wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element"); + return -1; + } + + if (full_config && conf->spectrum_mgmt_required && + conf->local_pwr_constraint == -1) { + wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements"); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) + return -1; + } + + return 0; +} + + +void hostapd_set_security_params(struct hostapd_bss_config *bss, + int full_config) +{ + if (bss->individual_wep_key_len == 0) { + /* individual keys are not use; can use key idx0 for + * broadcast keys */ + bss->broadcast_key_idx_min = 0; + } + + if ((bss->wpa & 2) && bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, + bss->rsn_pairwise); + + if (full_config) { + bss->radius->auth_server = bss->radius->auth_servers; + bss->radius->acct_server = bss->radius->acct_servers; + } + + if (bss->wpa && bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_WPA; + } else if (bss->wpa) { + bss->ssid.security_policy = SECURITY_WPA_PSK; + } else if (bss->ieee802_1x) { + int cipher = WPA_CIPHER_NONE; + bss->ssid.security_policy = SECURITY_IEEE_802_1X; + bss->ssid.wep.default_len = bss->default_wep_key_len; + if (full_config && bss->default_wep_key_len) { + cipher = bss->default_wep_key_len >= 13 ? + WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; + } else if (full_config && bss->ssid.wep.keys_set) { + if (bss->ssid.wep.len[0] >= 13) + cipher = WPA_CIPHER_WEP104; + else + cipher = WPA_CIPHER_WEP40; + } + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + if (full_config) + bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; + } else if (bss->ssid.wep.keys_set) { + int cipher = WPA_CIPHER_WEP40; + if (bss->ssid.wep.len[0] >= 13) + cipher = WPA_CIPHER_WEP104; + bss->ssid.security_policy = SECURITY_STATIC_WEP; + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + if (full_config) + bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; + } else if (bss->osen) { + bss->ssid.security_policy = SECURITY_OSEN; + bss->wpa_group = WPA_CIPHER_CCMP; + bss->wpa_pairwise = 0; + bss->rsn_pairwise = WPA_CIPHER_CCMP; + } else { + bss->ssid.security_policy = SECURITY_PLAINTEXT; + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; + if (full_config) + bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; + } +} diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index a1d2b048b513..961d2dd389f8 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -15,6 +15,34 @@ #include "common/ieee802_11_common.h" #include "wps/wps.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 *ies; + int 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 @@ -45,7 +73,8 @@ typedef enum hostap_security_policy { SECURITY_STATIC_WEP = 1, SECURITY_IEEE_802_1X = 2, SECURITY_WPA_PSK = 3, - SECURITY_WPA = 4 + SECURITY_WPA = 4, + SECURITY_OSEN = 5 } secpolicy; struct hostapd_ssid { @@ -53,6 +82,8 @@ struct hostapd_ssid { 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; @@ -74,8 +105,6 @@ struct hostapd_ssid { #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - struct hostapd_wep_keys **dyn_vlan_keys; - size_t max_dyn_vlan_keys; }; @@ -107,6 +136,7 @@ struct hostapd_wpa_psk { int group; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; }; struct hostapd_eap_user { @@ -124,7 +154,10 @@ struct hostapd_eap_user { 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 { @@ -180,6 +213,7 @@ struct hostapd_nai_realm_data { 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; @@ -187,11 +221,10 @@ struct hostapd_bss_config { unsigned int logger_syslog; /* module bitfield */ unsigned int logger_stdout; /* module bitfield */ - char *dump_log_name; /* file name for state dump (SIGUSR1) */ - 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; @@ -200,6 +233,7 @@ struct hostapd_bss_config { 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; @@ -226,6 +260,8 @@ struct hostapd_bss_config { 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 @@ -242,6 +278,7 @@ struct hostapd_bss_config { 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} */ @@ -250,6 +287,7 @@ struct hostapd_bss_config { 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) */ @@ -294,7 +332,9 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + 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; @@ -309,10 +349,9 @@ struct hostapd_bss_config { char *radius_server_clients; int radius_server_auth_port; + int radius_server_acct_port; int radius_server_ipv6; - char *test_socket; /* UNIX domain socket path for driver_test */ - int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group * address instead of individual address * (for driver_wired.c). @@ -324,7 +363,7 @@ struct hostapd_bss_config { int wmm_enabled; int wmm_uapsd; - struct hostapd_vlan *vlan, *vlan_tail; + struct hostapd_vlan *vlan; macaddr bssid; @@ -340,6 +379,7 @@ struct hostapd_bss_config { int wps_state; #ifdef CONFIG_WPS + int wps_independent; int ap_setup_locked; u8 uuid[16]; char *wps_pin_requests; @@ -356,6 +396,7 @@ struct hostapd_bss_config { 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; @@ -365,12 +406,14 @@ struct hostapd_bss_config { 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) @@ -378,6 +421,12 @@ struct hostapd_bss_config { #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; @@ -436,9 +485,15 @@ struct hostapd_bss_config { 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; #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; @@ -446,6 +501,32 @@ struct hostapd_bss_config { 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[HOSTAPD_MAX_SSID_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_*) */ @@ -455,6 +536,23 @@ struct hostapd_bss_config { #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; +#endif /* CONFIG_TESTING_OPTIONS */ + +#define MESH_ENABLED BIT(0) + int mesh; + + int radio_measurements; + + int vendor_vht; }; @@ -462,7 +560,7 @@ struct hostapd_bss_config { * struct hostapd_config - Per-radio interface configuration */ struct hostapd_config { - struct hostapd_bss_config *bss, *last_bss; + struct hostapd_bss_config **bss, *last_bss; size_t num_bss; u16 beacon_int; @@ -470,6 +568,7 @@ struct hostapd_config { int fragm_threshold; u8 send_probe_response; u8 channel; + int *chanlist; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -480,6 +579,7 @@ struct hostapd_config { int *basic_rates; const struct wpa_driver_ops *driver; + char *driver_params; int ap_table_max_size; int ap_table_expiration_time; @@ -493,6 +593,18 @@ struct hostapd_config { 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]; /* @@ -509,12 +621,34 @@ struct hostapd_config { int ieee80211n; int secondary_channel; 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_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 */ }; @@ -522,18 +656,24 @@ 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); -int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, - struct hostapd_wep_keys *b); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, - const u8 *addr, const u8 *prev_psk); + 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/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 02da25b71483..e16306c4e106 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -9,8 +9,8 @@ #include "utils/includes.h" #include "utils/common.h" -#include "drivers/driver.h" #include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" #include "wps/wps.h" #include "p2p/p2p.h" #include "hostapd.h" @@ -129,14 +129,14 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ -#ifdef CONFIG_WPS2 +#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_WPS2 */ +#endif /* CONFIG_WPS */ #ifdef CONFIG_P2P_MANAGER if (hapd->conf->p2p & P2P_MANAGE) { @@ -171,8 +171,27 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, 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; @@ -262,7 +281,8 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, params.wpa = hapd->conf->wpa; params.ieee802_1x = hapd->conf->ieee802_1x; params.wpa_group = hapd->conf->wpa_group; - params.wpa_pairwise = hapd->conf->wpa_pairwise; + params.wpa_pairwise = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; params.rsn_preauth = hapd->conf->rsn_preauth; #ifdef CONFIG_IEEE80211W @@ -278,7 +298,7 @@ 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); + NULL, NULL, force_ifname, if_addr, NULL, 0); } @@ -288,19 +308,19 @@ int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname) } -int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid, - int val) +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 0; + 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); + bridge, ifname_wds); } @@ -338,7 +358,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, const u8 *supp_rates, size_t supp_rates_len, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, - u32 flags, u8 qosinfo) + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo, u8 vht_opmode) { struct hostapd_sta_add_params params; @@ -355,6 +376,9 @@ int hostapd_sta_add(struct hostapd_data *hapd, 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, ¶ms); @@ -407,20 +431,21 @@ 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) + 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); + 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->driver->if_remove == NULL) + 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); } @@ -453,20 +478,25 @@ int hostapd_flush(struct hostapd_data *hapd) } -int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int sec_channel_offset) +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; - os_memset(&data, 0, sizeof(data)); - data.mode = mode; - data.freq = freq; - data.channel = channel; - data.ht_enabled = ht_enabled; - data.sec_channel_offset = sec_channel_offset; return hapd->driver->set_freq(hapd->drv_priv, &data); } @@ -616,7 +646,7 @@ 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 0; + return -1; return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, buf_len); } @@ -632,3 +662,66 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, 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); +} + + +int hostapd_drv_do_acs(struct hostapd_data *hapd) +{ + struct drv_acs_params params; + + if (hapd->driver == NULL || hapd->driver->do_acs == NULL) + return 0; + os_memset(¶ms, 0, sizeof(params)); + params.hw_mode = hapd->iface->conf->hw_mode; + params.ht_enabled = !!(hapd->iface->conf->ieee80211n); + params.ht40_enabled = !!(hapd->iface->conf->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); + return hapd->driver->do_acs(hapd->drv_priv, ¶ms); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 9c53b99d82a2..5d07e71f1bf1 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -1,6 +1,6 @@ /* * hostapd - Driver operations - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +13,8 @@ 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, @@ -30,14 +32,15 @@ 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, const u8 *addr, int aid, - int val); +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, - u32 flags, u8 qosinfo); + 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); @@ -46,7 +49,7 @@ 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); + 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, @@ -54,8 +57,10 @@ int hostapd_set_ieee8021x(struct hostapd_data *hapd, 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, int mode, int freq, - int channel, int ht_enabled, int sec_channel_offset); +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, @@ -97,6 +102,12 @@ 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" @@ -105,6 +116,9 @@ 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) { @@ -169,6 +183,14 @@ static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, 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) { @@ -213,4 +235,105 @@ static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, 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/src/ap/ap_list.c b/src/ap/ap_list.c index 18090ca18c0b..04a56a95efd9 100644 --- a/src/ap/ap_list.c +++ b/src/ap/ap_list.c @@ -14,7 +14,6 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" #include "hostapd.h" #include "ap_config.h" #include "ieee802_11.h" @@ -33,7 +32,8 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) { int i; - if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || + if (iface->current_mode == NULL || + iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || iface->conf->channel != ap->channel) return 0; @@ -50,7 +50,7 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) } -struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) +static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) { struct ap_info *s; @@ -87,34 +87,6 @@ static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) } -static void ap_ap_iter_list_add(struct hostapd_iface *iface, - struct ap_info *ap) -{ - if (iface->ap_iter_list) { - ap->iter_prev = iface->ap_iter_list->iter_prev; - iface->ap_iter_list->iter_prev = ap; - } else - ap->iter_prev = ap; - ap->iter_next = iface->ap_iter_list; - iface->ap_iter_list = ap; -} - - -static void ap_ap_iter_list_del(struct hostapd_iface *iface, - struct ap_info *ap) -{ - if (iface->ap_iter_list == ap) - iface->ap_iter_list = ap->iter_next; - else - ap->iter_prev->iter_next = ap->iter_next; - - if (ap->iter_next) - ap->iter_next->iter_prev = ap->iter_prev; - else if (iface->ap_iter_list) - iface->ap_iter_list->iter_prev = ap->iter_prev; -} - - static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) { ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; @@ -139,8 +111,8 @@ static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) if (s->hnext != NULL) s->hnext = s->hnext->hnext; else - printf("AP: could not remove AP " MACSTR " from hash table\n", - MAC2STR(ap->addr)); + wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR + " from hash table", MAC2STR(ap->addr)); } @@ -148,7 +120,6 @@ static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) { ap_ap_hash_del(iface, ap); ap_ap_list_del(iface, ap); - ap_ap_iter_list_del(iface, ap); iface->num_ap--; os_free(ap); @@ -171,25 +142,6 @@ static void hostapd_free_aps(struct hostapd_iface *iface) } -int ap_ap_for_each(struct hostapd_iface *iface, - int (*func)(struct ap_info *s, void *data), void *data) -{ - struct ap_info *s; - int ret = 0; - - s = iface->ap_list; - - while (s) { - ret = func(s, data); - if (ret) - break; - s = s->next; - } - - return ret; -} - - static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) { struct ap_info *ap; @@ -203,7 +155,6 @@ static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) ap_ap_list_add(iface, ap); iface->num_ap++; ap_ap_hash_add(iface, ap); - ap_ap_iter_list_add(iface, ap); if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { wpa_printf(MSG_DEBUG, "Removing the least recently used AP " @@ -221,9 +172,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, struct hostapd_frame_info *fi) { struct ap_info *ap; - struct os_time now; int new_ap = 0; - size_t len; int set_beacon = 0; if (iface->conf->ap_table_max_size < 1) @@ -233,30 +182,17 @@ void ap_list_process_beacon(struct hostapd_iface *iface, if (!ap) { ap = ap_ap_add(iface, mgmt->bssid); if (!ap) { - printf("Failed to allocate AP information entry\n"); + wpa_printf(MSG_INFO, + "Failed to allocate AP information entry"); return; } new_ap = 1; } - ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); - ap->capability = le_to_host16(mgmt->u.beacon.capab_info); - - if (elems->ssid) { - len = elems->ssid_len; - if (len >= sizeof(ap->ssid)) - len = sizeof(ap->ssid) - 1; - os_memcpy(ap->ssid, elems->ssid, len); - ap->ssid[len] = '\0'; - ap->ssid_len = len; - } - merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, elems->supp_rates, elems->supp_rates_len, elems->ext_supp_rates, elems->ext_supp_rates_len); - ap->wpa = elems->wpa_ie != NULL; - if (elems->erp_info && elems->erp_info_len == 1) ap->erp = elems->erp_info[0]; else @@ -264,6 +200,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface, if (elems->ds_params && elems->ds_params_len == 1) ap->channel = elems->ds_params[0]; + else if (elems->ht_operation && elems->ht_operation_len >= 1) + ap->channel = elems->ht_operation[0]; else if (fi) ap->channel = fi->channel; @@ -272,11 +210,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, else ap->ht_support = 0; - ap->num_beacons++; - os_get_time(&now); - ap->last_beacon = now.sec; - if (fi) - ap->datarate = fi->datarate; + os_get_reltime(&ap->last_beacon); if (!new_ap && ap != iface->ap_list) { /* move AP entry into the beginning of the list so that the @@ -288,17 +222,23 @@ void ap_list_process_beacon(struct hostapd_iface *iface, if (!iface->olbc && ap_list_beacon_olbc(iface, ap)) { iface->olbc = 1; - wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " - "protection", MAC2STR(ap->addr)); + wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR + " (channel %d) - enable protection", + MAC2STR(ap->addr), ap->channel); set_beacon++; } #ifdef CONFIG_IEEE80211N - if (!iface->olbc_ht && !ap->ht_support) { + if (!iface->olbc_ht && !ap->ht_support && + (ap->channel == 0 || + ap->channel == iface->conf->channel || + ap->channel == iface->conf->channel + + iface->conf->secondary_channel * 4)) { iface->olbc_ht = 1; hostapd_ht_operation_update(iface); wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR - " - enable protection", MAC2STR(ap->addr)); + " (channel %d) - enable protection", + MAC2STR(ap->addr), ap->channel); set_beacon++; } #endif /* CONFIG_IEEE80211N */ @@ -311,7 +251,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface, static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_iface *iface = eloop_ctx; - struct os_time now; + struct os_reltime now; struct ap_info *ap; int set_beacon = 0; @@ -320,12 +260,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) if (!iface->ap_list) return; - os_get_time(&now); + os_get_reltime(&now); while (iface->ap_list) { ap = iface->ap_list->prev; - if (ap->last_beacon + iface->conf->ap_table_expiration_time >= - now.sec) + if (!os_reltime_expired(&now, &ap->last_beacon, + iface->conf->ap_table_expiration_time)) break; ap_free_ap(iface, ap); diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h index f0b41259bda6..93dc0eda88d3 100644 --- a/src/ap/ap_list.h +++ b/src/ap/ap_list.h @@ -14,42 +14,24 @@ struct ap_info { /* Note: next/prev pointers are updated whenever a new beacon is * received because these are used to find the least recently used - * entries. iter_next/iter_prev are updated only when adding new BSSes - * and when removing old ones. These should be used when iterating - * through the table in a manner that allows beacons to be received - * during the iteration. */ + * entries. */ struct ap_info *next; /* next entry in AP list */ struct ap_info *prev; /* previous entry in AP list */ struct ap_info *hnext; /* next entry in hash table list */ - struct ap_info *iter_next; /* next entry in AP iteration list */ - struct ap_info *iter_prev; /* previous entry in AP iteration list */ u8 addr[6]; - u16 beacon_int; - u16 capability; u8 supported_rates[WLAN_SUPP_RATES_MAX]; - u8 ssid[33]; - size_t ssid_len; - int wpa; int erp; /* ERP Info or -1 if ERP info element not present */ int channel; - int datarate; /* in 100 kbps */ int ht_support; - unsigned int num_beacons; /* number of beacon frames received */ - os_time_t last_beacon; - - int already_seen; /* whether API call AP-NEW has already fetched - * information about this AP */ + struct os_reltime last_beacon; }; struct ieee802_11_elems; struct hostapd_frame_info; -struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *sta); -int ap_ap_for_each(struct hostapd_iface *iface, - int (*func)(struct ap_info *s, void *data), void *data); void ap_list_process_beacon(struct hostapd_iface *iface, const struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index a9596947fafb..13604edc4940 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -16,6 +16,7 @@ #include "wpa_auth.h" #include "sta_info.h" #include "ap_mlme.h" +#include "hostapd.h" #ifndef CONFIG_NO_HOSTAPD_LOGGER @@ -80,7 +81,8 @@ void mlme_deauthenticate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", MAC2STR(sta->addr), reason_code); - mlme_deletekeys_request(hapd, sta); + if (!hapd->iface->driver_ap_teardown) + mlme_deletekeys_request(hapd, sta); } @@ -118,8 +120,6 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) * reassociation procedure that was initiated by that specific peer MAC entity. * * PeerSTAAddress = sta->addr - * - * sta->previous_ap contains the "Current AP" information from ReassocReq. */ void mlme_reassociate_indication(struct hostapd_data *hapd, struct sta_info *sta) diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index d66d97e4a0db..bd1778e41865 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -79,7 +79,10 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; + user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; + user->remediation = eap_user->remediation; + user->accept_attr = eap_user->accept_attr; return 0; } @@ -92,6 +95,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) os_memset(&srv, 0, sizeof(srv)); srv.client_file = conf->radius_server_clients; srv.auth_port = conf->radius_server_auth_port; + srv.acct_port = conf->radius_server_acct_port; srv.conf_ctx = hapd; srv.eap_sim_db_priv = hapd->eap_sim_db_priv; srv.ssl_ctx = hapd->ssl_ctx; @@ -111,9 +115,17 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.eap_req_id_text = conf->eap_req_id_text; srv.eap_req_id_text_len = conf->eap_req_id_text_len; srv.pwd_group = conf->pwd_group; + srv.server_id = conf->server_id ? conf->server_id : "hostapd"; + srv.sqlite_file = conf->eap_user_sqlite; #ifdef CONFIG_RADIUS_TEST srv.dump_msk_file = conf->dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_HS20 + srv.subscr_remediation_url = conf->subscr_remediation_url; + srv.subscr_remediation_method = conf->subscr_remediation_method; +#endif /* CONFIG_HS20 */ + srv.erp = conf->eap_server_erp; + srv.erp_domain = conf->erp_domain; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -132,7 +144,7 @@ int authsrv_init(struct hostapd_data *hapd) #ifdef EAP_TLS_FUNCS if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert || - hapd->conf->dh_file)) { + hapd->conf->private_key || hapd->conf->dh_file)) { struct tls_connection_params params; hapd->ssl_ctx = tls_init(NULL); @@ -148,6 +160,9 @@ int authsrv_init(struct hostapd_data *hapd) params.private_key = hapd->conf->private_key; params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; + params.openssl_ciphers = hapd->conf->openssl_ciphers; + params.ocsp_stapling_response = + hapd->conf->ocsp_stapling_response; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 4c47c75841c5..e575b65cbf3a 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -4,14 +4,8 @@ * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -21,7 +15,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" +#include "common/hw_features_common.h" #include "wps/wps_defs.h" #include "p2p/p2p.h" #include "hostapd.h" @@ -34,10 +28,56 @@ #include "ap_drv_ops.h" #include "beacon.h" #include "hs20.h" +#include "dfs.h" #ifdef NEED_AP_MLME +static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + if (!hapd->conf->radio_measurements || len < 2 + 4) + return eid; + + *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *eid++ = 5; + *eid++ = (hapd->conf->radio_measurements & BIT(0)) ? + WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00; + *eid++ = 0x00; + *eid++ = 0x00; + *eid++ = 0x00; + *eid++ = 0x00; + return eid; +} + + +static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + if (len < 2 + 5) + return eid; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->bss_load_test_set) { + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + os_memcpy(eid, hapd->conf->bss_load_test, 5); + eid += 5; + return eid; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (hapd->conf->bss_load_update_period) { + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + WPA_PUT_LE16(eid, hapd->num_sta); + eid += 2; + *eid++ = hapd->iface->channel_utilization; + WPA_PUT_LE16(eid, 0); /* no available admission capabity */ + eid += 2; + } + return eid; +} + + static u8 ieee802_11_erp_info(struct hostapd_data *hapd) { u8 erp = 0; @@ -93,6 +133,74 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) } +static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 local_pwr_constraint = 0; + int dfs; + + if (hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) + return eid; + + /* Let host drivers add this IE if DFS support is offloaded */ + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return eid; + + /* + * There is no DFS support and power constraint was not directly + * requested by config option. + */ + if (!hapd->iconf->ieee80211h && + hapd->iconf->local_pwr_constraint == -1) + return eid; + + /* Check if DFS is required by regulatory. */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) { + wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", + dfs); + dfs = 0; + } + + if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1) + return eid; + + /* + * ieee80211h (DFS) is enabled so Power Constraint element shall + * be added when running on DFS channel whenever local_pwr_constraint + * is configured or not. In order to meet regulations when TPC is not + * implemented using a transmit power that is below the legal maximum + * (including any mitigation factor) should help. In this case, + * indicate 3 dB below maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = 3; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + + /* Element ID */ + *pos++ = WLAN_EID_PWR_CONSTRAINT; + /* Length */ + *pos++ = 1; + /* Local Power Constraint */ + if (local_pwr_constraint) + *pos++ = local_pwr_constraint; + else + *pos++ = hapd->iconf->local_pwr_constraint; + + return pos; +} + + static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, struct hostapd_channel_data *start, struct hostapd_channel_data *prev) @@ -146,7 +254,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, continue; /* can use same entry */ } - if (start) { + if (start && prev) { pos = hostapd_eid_country_add(pos, end, chan_spacing, start, prev); start = NULL; @@ -187,6 +295,70 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) } +static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) +{ + u8 chan; + + if (!hapd->cs_freq_params.freq) + return eid; + + if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) == + NUM_HOSTAPD_MODES) + return eid; + + *eid++ = WLAN_EID_CHANNEL_SWITCH; + *eid++ = 3; + *eid++ = hapd->cs_block_tx; + *eid++ = chan; + *eid++ = hapd->cs_count; + + return eid; +} + + +static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +{ + u8 sec_ch; + + if (!hapd->cs_freq_params.sec_channel_offset) + return eid; + + if (hapd->cs_freq_params.sec_channel_offset == -1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; + else if (hapd->cs_freq_params.sec_channel_offset == 1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; + else + return eid; + + *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; + *eid++ = 1; + *eid++ = sec_ch; + + return eid; +} + + +static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, + u8 *start, unsigned int *csa_counter_off) +{ + u8 *old_pos = pos; + + if (!csa_counter_off) + return pos; + + *csa_counter_off = 0; + pos = hostapd_eid_csa(hapd, pos); + + if (pos != old_pos) { + /* save an offset to the counter - should be last byte */ + *csa_counter_off = pos - start - 1; + pos = hostapd_eid_secondary_channel(hapd, pos); + } + + return pos; +} + + static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *req, @@ -208,6 +380,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); + if (hapd->conf->vendor_vht) { + buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -242,6 +418,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_country(hapd, pos, epos - pos); + /* Power Constraint element */ + pos = hostapd_eid_pwr_constraint(hapd, pos); + /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -251,6 +430,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* RSN, MDIE, WPA */ pos = hostapd_eid_wpa(hapd, pos, epos - pos); + pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + + pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + #ifdef CONFIG_IEEE80211N pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); @@ -265,9 +448,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); + pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, + &hapd->cs_c_off_proberesp); #ifdef CONFIG_IEEE80211AC - pos = hostapd_eid_vht_capabilities(hapd, pos); - pos = hostapd_eid_vht_operation(hapd, pos); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); + } + if (hapd->conf->vendor_vht) + pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ @@ -297,6 +486,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_HS20 pos = hostapd_eid_hs20_indication(hapd, pos); + pos = hostapd_eid_osen(hapd, pos); #endif /* CONFIG_HS20 */ if (hapd->conf->vendor_elements) { @@ -390,6 +580,27 @@ void handle_probe_req(struct hostapd_data *hapd, return; } + /* + * No need to reply if the Probe Request frame was sent on an adjacent + * channel. IEEE Std 802.11-2012 describes this as a requirement for an + * AP with dot11RadioMeasurementActivated set to true, but strictly + * speaking does not allow such ignoring of Probe Request frames if + * dot11RadioMeasurementActivated is false. Anyway, this can help reduce + * number of unnecessary Probe Response frames for cases where the STA + * is less likely to see them (Probe Request frame sent on a + * neighboring, but partially overlapping, channel). + */ + if (elems.ds_params && elems.ds_params_len == 1 && + hapd->iface->current_mode && + (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G || + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) && + hapd->iconf->channel != elems.ds_params[0]) { + wpa_printf(MSG_DEBUG, + "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u", + hapd->iconf->channel, elems.ds_params[0]); + return; + } + #ifdef CONFIG_P2P if (hapd->p2p && elems.wps_ie) { struct wpabuf *wps; @@ -443,12 +654,10 @@ void handle_probe_req(struct hostapd_data *hapd, sta->ssid_probe = &hapd->conf->ssid; } else { if (!(mgmt->da[0] & 0x01)) { - char ssid_txt[33]; - ieee802_11_print_ssid(ssid_txt, elems.ssid, - elems.ssid_len); wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for foreign SSID '%s' (DA " MACSTR ")%s", - MAC2STR(mgmt->sa), ssid_txt, + MAC2STR(mgmt->sa), + wpa_ssid_txt(elems.ssid, elems.ssid_len), MAC2STR(mgmt->da), elems.ssid_list ? " (SSID list)" : ""); } @@ -456,7 +665,8 @@ void handle_probe_req(struct hostapd_data *hapd, } #ifdef CONFIG_INTERWORKING - if (elems.interworking && elems.interworking_len >= 1) { + if (hapd->conf->interworking && + elems.interworking && elems.interworking_len >= 1) { u8 ant = elems.interworking[0] & 0x0f; if (ant != INTERWORKING_ANT_WILDCARD && ant != hapd->conf->access_network_type) { @@ -467,7 +677,7 @@ void handle_probe_req(struct hostapd_data *hapd, } } - if (elems.interworking && + if (hapd->conf->interworking && elems.interworking && (elems.interworking_len == 7 || elems.interworking_len == 9)) { const u8 *hessid; if (elems.interworking_len == 7) @@ -485,9 +695,30 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " + MACSTR " with only 802.11b rates", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_P2P */ + /* TODO: verify that supp_rates contains at least one matching rate * with AP configuration */ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_probe_probability > 0.0 && + drand48() < hapd->iconf->ignore_probe_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring probe request from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) @@ -501,7 +732,7 @@ void handle_probe_req(struct hostapd_data *hapd, is_broadcast_ether_addr(mgmt->da)); if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) - perror("handle_probe_req: send"); + wpa_printf(MSG_INFO, "handle_probe_req: send failed"); os_free(resp); @@ -549,23 +780,17 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ -void ieee802_11_set_beacon(struct hostapd_data *hapd) +int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) { struct ieee80211_mgmt *head = NULL; u8 *tail = NULL; size_t head_len = 0, tail_len = 0; u8 *resp = NULL; size_t resp_len = 0; - struct wpa_driver_ap_params params; - struct wpabuf *beacon, *proberesp, *assocresp; #ifdef NEED_AP_MLME u16 capab_info; u8 *pos, *tailpos; -#endif /* NEED_AP_MLME */ - - hapd->beacon_set_done = 1; - -#ifdef NEED_AP_MLME #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -581,12 +806,20 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); + +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht) { + tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } +#endif /* CONFIG_IEEE80211AC */ + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); os_free(head); os_free(tail); - return; + return -1; } head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -631,6 +864,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_country(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + /* Power Constraint element */ + tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); + /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); @@ -641,6 +877,13 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); + + tailpos = hostapd_eid_bss_load(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + #ifdef CONFIG_IEEE80211N tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); @@ -657,10 +900,15 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_interworking(hapd, tailpos); tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); - + tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, + &hapd->cs_c_off_beacon); #ifdef CONFIG_IEEE80211AC - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); - tailpos = hostapd_eid_vht_operation(hapd, tailpos); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); + } + if (hapd->conf->vendor_vht) + tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ @@ -689,6 +937,7 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) #ifdef CONFIG_HS20 tailpos = hostapd_eid_hs20_indication(hapd, tailpos); + tailpos = hostapd_eid_osen(hapd, tailpos); #endif /* CONFIG_HS20 */ if (hapd->conf->vendor_elements) { @@ -702,94 +951,168 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) resp = hostapd_probe_resp_offloads(hapd, &resp_len); #endif /* NEED_AP_MLME */ - os_memset(¶ms, 0, sizeof(params)); - params.head = (u8 *) head; - params.head_len = head_len; - params.tail = tail; - params.tail_len = tail_len; - params.proberesp = resp; - params.proberesp_len = resp_len; - params.dtim_period = hapd->conf->dtim_period; - params.beacon_int = hapd->iconf->beacon_int; - params.basic_rates = hapd->iface->basic_rates; - params.ssid = hapd->conf->ssid.ssid; - params.ssid_len = hapd->conf->ssid.ssid_len; - params.pairwise_ciphers = hapd->conf->rsn_pairwise ? - hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; - params.group_cipher = hapd->conf->wpa_group; - params.key_mgmt_suites = hapd->conf->wpa_key_mgmt; - params.auth_algs = hapd->conf->auth_algs; - params.wpa_version = hapd->conf->wpa; - params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || + os_memset(params, 0, sizeof(*params)); + params->head = (u8 *) head; + params->head_len = head_len; + params->tail = tail; + params->tail_len = tail_len; + params->proberesp = resp; + params->proberesp_len = resp_len; + params->dtim_period = hapd->conf->dtim_period; + params->beacon_int = hapd->iconf->beacon_int; + params->basic_rates = hapd->iface->basic_rates; + params->ssid = hapd->conf->ssid.ssid; + params->ssid_len = hapd->conf->ssid.ssid_len; + params->pairwise_ciphers = hapd->conf->wpa_pairwise | + hapd->conf->rsn_pairwise; + params->group_cipher = hapd->conf->wpa_group; + params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params->auth_algs = hapd->conf->auth_algs; + params->wpa_version = hapd->conf->wpa; + params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || (hapd->conf->ieee802_1x && (hapd->conf->default_wep_key_len || hapd->conf->individual_wep_key_len)); switch (hapd->conf->ignore_broadcast_ssid) { case 0: - params.hide_ssid = NO_SSID_HIDING; + params->hide_ssid = NO_SSID_HIDING; break; case 1: - params.hide_ssid = HIDDEN_SSID_ZERO_LEN; + params->hide_ssid = HIDDEN_SSID_ZERO_LEN; break; case 2: - params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; + params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; break; } - hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp); - params.beacon_ies = beacon; - params.proberesp_ies = proberesp; - params.assocresp_ies = assocresp; - params.isolate = hapd->conf->isolate; + params->isolate = hapd->conf->isolate; + params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK; #ifdef NEED_AP_MLME - params.cts_protect = !!(ieee802_11_erp_info(hapd) & + params->cts_protect = !!(ieee802_11_erp_info(hapd) & ERP_INFO_USE_PROTECTION); - params.preamble = hapd->iface->num_sta_no_short_preamble == 0 && + params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE; if (hapd->iface->current_mode && hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) - params.short_slot_time = + params->short_slot_time = hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; else - params.short_slot_time = -1; + params->short_slot_time = -1; if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) - params.ht_opmode = -1; + params->ht_opmode = -1; else - params.ht_opmode = hapd->iface->ht_op_mode; + params->ht_opmode = hapd->iface->ht_op_mode; #endif /* NEED_AP_MLME */ - params.interworking = hapd->conf->interworking; + params->interworking = hapd->conf->interworking; if (hapd->conf->interworking && !is_zero_ether_addr(hapd->conf->hessid)) - params.hessid = hapd->conf->hessid; - params.access_network_type = hapd->conf->access_network_type; - params.ap_max_inactivity = hapd->conf->ap_max_inactivity; + params->hessid = hapd->conf->hessid; + params->access_network_type = hapd->conf->access_network_type; + params->ap_max_inactivity = hapd->conf->ap_max_inactivity; +#ifdef CONFIG_P2P + params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow; +#endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 - params.disable_dgaf = hapd->conf->disable_dgaf; + params->disable_dgaf = hapd->conf->disable_dgaf; + if (hapd->conf->osen) { + params->privacy = 1; + params->osen = 1; + } #endif /* CONFIG_HS20 */ - if (hostapd_drv_set_ap(hapd, ¶ms)) - wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); - hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + return 0; +} - os_free(tail); - os_free(head); - os_free(resp); + +void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) +{ + os_free(params->tail); + params->tail = NULL; + os_free(params->head); + params->head = NULL; + os_free(params->proberesp); + params->proberesp = NULL; } -void ieee802_11_set_beacons(struct hostapd_iface *iface) +int ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct wpa_driver_ap_params params; + struct hostapd_freq_params freq; + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct wpabuf *beacon, *proberesp, *assocresp; + int res, ret = -1; + + if (hapd->csa_in_progress) { + wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); + return -1; + } + + hapd->beacon_set_done = 1; + + if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) + return -1; + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + goto fail; + + params.beacon_ies = beacon; + params.proberesp_ies = proberesp; + params.assocresp_ies = assocresp; + params.reenable = hapd->reenable_beacon; + hapd->reenable_beacon = 0; + + if (iface->current_mode && + hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq, + iconf->channel, iconf->ieee80211n, + iconf->ieee80211ac, + iconf->secondary_channel, + iconf->vht_oper_chwidth, + iconf->vht_oper_centr_freq_seg0_idx, + iconf->vht_oper_centr_freq_seg1_idx, + iface->current_mode->vht_capab) == 0) + params.freq = &freq; + + res = hostapd_drv_set_ap(hapd, ¶ms); + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + if (res) + wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + else + ret = 0; +fail: + ieee802_11_free_ap_params(¶ms); + return ret; +} + + +int ieee802_11_set_beacons(struct hostapd_iface *iface) { size_t i; - for (i = 0; i < iface->num_bss; i++) - ieee802_11_set_beacon(iface->bss[i]); + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; } /* only update beacons if started */ -void ieee802_11_update_beacons(struct hostapd_iface *iface) +int ieee802_11_update_beacons(struct hostapd_iface *iface) { size_t i; - for (i = 0; i < iface->num_bss; i++) - if (iface->bss[i]->beacon_set_done) - ieee802_11_set_beacon(iface->bss[i]); + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; } #endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/src/ap/beacon.h b/src/ap/beacon.h index 37f10d2f587a..722159a7500c 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -3,14 +3,8 @@ * Copyright (c) 2002-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BEACON_H @@ -21,8 +15,11 @@ struct ieee80211_mgmt; void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal); -void ieee802_11_set_beacon(struct hostapd_data *hapd); -void ieee802_11_set_beacons(struct hostapd_iface *iface); -void ieee802_11_update_beacons(struct hostapd_iface *iface); +int ieee802_11_set_beacon(struct hostapd_data *hapd); +int ieee802_11_set_beacons(struct hostapd_iface *iface); +int ieee802_11_update_beacons(struct hostapd_iface *iface); +int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params); +void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); #endif /* BEACON_H */ diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c new file mode 100644 index 000000000000..fb639423230c --- /dev/null +++ b/src/ap/bss_load.c @@ -0,0 +1,65 @@ +/* + * BSS Load Element / Channel Utilization + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * 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 "hostapd.h" +#include "bss_load.h" +#include "ap_drv_ops.h" +#include "beacon.h" + + +static void update_channel_utilization(void *eloop_data, void *user_data) +{ + struct hostapd_data *hapd = eloop_data; + unsigned int sec, usec; + int err; + + if (!(hapd->beacon_set_done && hapd->started)) + return; + + err = hostapd_drv_get_survey(hapd, hapd->iface->freq); + if (err) { + wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data"); + return; + } + + ieee802_11_set_beacon(hapd); + + sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; + usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); +} + + +int bss_load_update_init(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + struct hostapd_config *iconf = hapd->iconf; + unsigned int sec, usec; + + if (!conf->bss_load_update_period || !iconf->beacon_int) + return -1; + + hapd->bss_load_update_timeout = conf->bss_load_update_period * + iconf->beacon_int; + sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; + usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); + return 0; +} + + +void bss_load_update_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(update_channel_utilization, hapd, NULL); +} diff --git a/src/ap/bss_load.h b/src/ap/bss_load.h new file mode 100644 index 000000000000..ac3c793c90b1 --- /dev/null +++ b/src/ap/bss_load.h @@ -0,0 +1,17 @@ +/* + * BSS load update + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BSS_LOAD_UPDATE_H +#define BSS_LOAD_UPDATE_H + + +int bss_load_update_init(struct hostapd_data *hapd); +void bss_load_update_deinit(struct hostapd_data *hapd); + + +#endif /* BSS_LOAD_UPDATE_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index c55d3fe32a61..41ab988277bb 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -1,6 +1,6 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,8 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/sae.h" +#include "eapol_auth/eapol_auth_sm.h" #include "hostapd.h" #include "ieee802_1x.h" #include "wpa_auth.h" @@ -21,25 +23,61 @@ #include "ap_drv_ops.h" +static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + struct hostap_sta_driver_data data; + int ret; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return 0; + + ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" + "rx_bytes=%lu\ntx_bytes=%lu\n", + data.rx_packets, data.tx_packets, + data.rx_bytes, data.tx_bytes); + if (os_snprintf_error(buflen, ret)) + return 0; + return ret; +} + + static int hostapd_get_sta_conn_time(struct sta_info *sta, char *buf, size_t buflen) { - struct os_time now, age; - int len = 0, ret; + struct os_reltime age; + int ret; if (!sta->connected_time.sec) return 0; - os_get_time(&now); - os_time_sub(&now, &sta->connected_time, &age); + os_reltime_age(&sta->connected_time, &age); - ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n", + ret = os_snprintf(buf, buflen, "connected_time=%u\n", (unsigned int) age.sec); - if (ret < 0 || (size_t) ret >= buflen - len) - return len; - len += ret; + if (os_snprintf_error(buflen, ret)) + return 0; + return ret; +} - return len; + +static const char * timeout_next_str(int val) +{ + switch (val) { + case STA_NULLFUNC: + return "NULLFUNC POLL"; + case STA_DISASSOC: + return "DISASSOC"; + case STA_DEAUTH: + return "DEAUTH"; + case STA_REMOVE: + return "REMOVE"; + case STA_DISASSOC_FROM_CLI: + return "DISASSOC_FROM_CLI"; + } + + return "?"; } @@ -47,19 +85,42 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { - int len, res, ret; + int len, res, ret, i; - if (sta == NULL) { - ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) - return 0; - return ret; - } + if (!sta) + return 0; len = 0; - ret = os_snprintf(buf + len, buflen - len, MACSTR "\n", + ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", MAC2STR(sta->addr)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); + if (ret < 0) + return len; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" + "listen_interval=%d\nsupported_rates=", + sta->aid, sta->capability, sta->listen_interval); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + for (i = 0; i < sta->supported_rates_len; i++) { + ret = os_snprintf(buf + len, buflen - len, "%02x%s", + sta->supported_rates[i], + i + 1 < sta->supported_rates_len ? " " : ""); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", + timeout_next_str(sta->timeout_next)); + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -80,9 +141,17 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, if (res >= 0) len += res; - res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len); - if (res >= 0) - len += res; + len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); + len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); + +#ifdef CONFIG_SAE + if (sta->sae && sta->sae->state == SAE_ACCEPTED) { + res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n", + sta->sae->group); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } +#endif /* CONFIG_SAE */ return len; } @@ -100,15 +169,37 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, { u8 addr[ETH_ALEN]; int ret; + const char *pos; + struct sta_info *sta; if (hwaddr_aton(txtaddr, addr)) { ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; return ret; } - return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), - buf, buflen); + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return -1; + + pos = os_strchr(txtaddr, ' '); + if (pos) { + pos++; + +#ifdef HOSTAPD_DUMP_STATE + if (os_strcmp(pos, "eapol") == 0) { + if (sta->eapol_sm == NULL) + return -1; + return eapol_auth_dump_state(sta->eapol_sm, buf, + buflen); + } +#endif /* HOSTAPD_DUMP_STATE */ + + return -1; + } + + return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen); } @@ -122,10 +213,14 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, if (hwaddr_aton(txtaddr, addr) || (sta = ap_get_sta(hapd, addr)) == NULL) { ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; return ret; - } + } + + if (!sta->next) + return 0; + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); } @@ -145,11 +240,12 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, if (mgmt == NULL) return -1; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR - " with minor reason code %u (stype=%u)", - MAC2STR(addr), minor_reason_code, stype); + " with minor reason code %u (stype=%u (%s))", + MAC2STR(addr), minor_reason_code, stype, + fc2str(mgmt->frame_control)); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); 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); @@ -165,9 +261,8 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 4 + 3 + 1; - WPA_PUT_BE24(pos, OUI_WFA); - pos += 3; - *pos++ = P2P_OUI_TYPE; + WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE); + pos += 4; *pos++ = P2P_ATTR_MINOR_REASON_CODE; WPA_PUT_LE16(pos, 1); @@ -189,6 +284,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, u8 addr[ETH_ALEN]; struct sta_info *sta; const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr); @@ -196,6 +292,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, if (hwaddr_aton(txtaddr, addr)) return -1; + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + pos = os_strstr(txtaddr, " test="); if (pos) { struct ieee80211_mgmt mgmt; @@ -210,8 +310,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, 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.deauth.reason_code = - host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + mgmt.u.deauth.reason_code = host_to_le16(reason); if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), @@ -228,11 +327,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) - ap_sta_deauthenticate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_deauthenticate(hapd, sta, reason); else if (addr[0] == 0xff) hostapd_free_stas(hapd); @@ -246,6 +344,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, u8 addr[ETH_ALEN]; struct sta_info *sta; const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr); @@ -253,6 +352,10 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, if (hwaddr_aton(txtaddr, addr)) return -1; + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + pos = os_strstr(txtaddr, " test="); if (pos) { struct ieee80211_mgmt mgmt; @@ -267,8 +370,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, 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.disassoc.reason_code = - host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + mgmt.u.disassoc.reason_code = host_to_le16(reason); if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), @@ -285,13 +387,159 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) - ap_sta_disassociate(hapd, sta, - WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_disassociate(hapd, sta, reason); else if (addr[0] == 0xff) hostapd_free_stas(hapd); return 0; } + + +int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + struct hostapd_iface *iface = hapd->iface; + int len = 0, ret; + size_t i; + + ret = os_snprintf(buf + len, buflen - len, + "state=%s\n" + "phy=%s\n" + "freq=%d\n" + "num_sta_non_erp=%d\n" + "num_sta_no_short_slot_time=%d\n" + "num_sta_no_short_preamble=%d\n" + "olbc=%d\n" + "num_sta_ht_no_gf=%d\n" + "num_sta_no_ht=%d\n" + "num_sta_ht_20_mhz=%d\n" + "num_sta_ht40_intolerant=%d\n" + "olbc_ht=%d\n" + "ht_op_mode=0x%x\n", + hostapd_state_text(iface->state), + iface->phy, + iface->freq, + iface->num_sta_non_erp, + iface->num_sta_no_short_slot_time, + iface->num_sta_no_short_preamble, + iface->olbc, + iface->num_sta_ht_no_gf, + iface->num_sta_no_ht, + iface->num_sta_ht_20mhz, + iface->num_sta_ht40_intolerant, + iface->olbc_ht, + iface->ht_op_mode); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + if (!iface->cac_started || !iface->dfs_cac_ms) { + ret = os_snprintf(buf + len, buflen - len, + "cac_time_seconds=%d\n" + "cac_time_left_seconds=N/A\n", + iface->dfs_cac_ms / 1000); + } else { + /* CAC started and CAC time set - calculate remaining time */ + struct os_reltime now; + unsigned int left_time; + + os_reltime_age(&iface->dfs_cac_start, &now); + left_time = iface->dfs_cac_ms / 1000 - now.sec; + ret = os_snprintf(buf + len, buflen - len, + "cac_time_seconds=%u\n" + "cac_time_left_seconds=%u\n", + iface->dfs_cac_ms / 1000, + left_time); + } + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, + "channel=%u\n" + "secondary_channel=%d\n" + "ieee80211n=%d\n" + "ieee80211ac=%d\n" + "vht_oper_chwidth=%d\n" + "vht_oper_centr_freq_seg0_idx=%d\n" + "vht_oper_centr_freq_seg1_idx=%d\n", + iface->conf->channel, + iface->conf->secondary_channel, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + ret = os_snprintf(buf + len, buflen - len, + "bss[%d]=%s\n" + "bssid[%d]=" MACSTR "\n" + "ssid[%d]=%s\n" + "num_sta[%d]=%d\n", + (int) i, bss->conf->iface, + (int) i, MAC2STR(bss->own_addr), + (int) i, + wpa_ssid_txt(bss->conf->ssid.ssid, + bss->conf->ssid.ssid_len), + (int) i, bss->num_sta); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + return len; +} + + +int hostapd_parse_csa_settings(const char *pos, + struct csa_settings *settings) +{ + char *end; + + os_memset(settings, 0, sizeof(*settings)); + settings->cs_count = strtol(pos, &end, 10); + if (pos == end) { + wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); + return -1; + } + + settings->freq_params.freq = atoi(end); + if (settings->freq_params.freq == 0) { + wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); + return -1; + } + +#define SET_CSA_SETTING(str) \ + do { \ + const char *pos2 = os_strstr(pos, " " #str "="); \ + if (pos2) { \ + pos2 += sizeof(" " #str "=") - 1; \ + settings->freq_params.str = atoi(pos2); \ + } \ + } while (0) + + SET_CSA_SETTING(center_freq1); + SET_CSA_SETTING(center_freq2); + SET_CSA_SETTING(bandwidth); + SET_CSA_SETTING(sec_channel_offset); + settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); + settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); + settings->block_tx = !!os_strstr(pos, " blocktx"); +#undef SET_CSA_SETTING + + return 0; +} + + +int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) +{ + return hostapd_drv_stop_ap(hapd); +} diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index e83f894144c5..e5297d03e810 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -1,6 +1,6 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,5 +19,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, const char *txtaddr); int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, const char *txtaddr); +int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, + size_t buflen); +int hostapd_parse_csa_settings(const char *pos, + struct csa_settings *settings); +int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); #endif /* CTRL_IFACE_AP_H */ diff --git a/src/ap/dfs.c b/src/ap/dfs.c new file mode 100644 index 000000000000..da6fd4646710 --- /dev/null +++ b/src/ap/dfs.c @@ -0,0 +1,1064 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2013-2015, Qualcomm Atheros, Inc. + * + * 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 "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "drivers/driver.h" +#include "dfs.h" + + +static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) +{ + int n_chans = 1; + + *seg1 = 0; + + if (iface->conf->ieee80211n && iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + case VHT_CHANWIDTH_80P80MHZ: + n_chans = 4; + *seg1 = 4; + break; + default: + break; + } + } + + return n_chans; +} + + +static int dfs_channel_available(struct hostapd_channel_data *chan, + int skip_radar) +{ + /* + * When radar detection happens, CSA is performed. However, there's no + * time for CAC, so radar channels must be skipped when finding a new + * channel for CSA, unless they are available for immediate use. + */ + if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) && + ((chan->flag & HOSTAPD_CHAN_DFS_MASK) != + HOSTAPD_CHAN_DFS_AVAILABLE)) + return 0; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return 0; + if ((chan->flag & HOSTAPD_CHAN_RADAR) && + ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE)) + return 0; + return 1; +} + + +static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) +{ + /* + * The tables contain first valid channel number based on channel width. + * We will also choose this first channel as the control one. + */ + int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + /* + * VHT80, valid channels based on center frequency: + * 42, 58, 106, 122, 138, 155 + */ + int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; + /* + * VHT160 valid channels based on center frequency: + * 50, 114 + */ + int allowed_160[] = { 36, 100 }; + int *allowed = allowed_40; + unsigned int i, allowed_no = 0; + + switch (n_chans) { + case 2: + allowed = allowed_40; + allowed_no = ARRAY_SIZE(allowed_40); + break; + case 4: + allowed = allowed_80; + allowed_no = ARRAY_SIZE(allowed_80); + break; + case 8: + allowed = allowed_160; + allowed_no = ARRAY_SIZE(allowed_160); + break; + default: + wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); + break; + } + + for (i = 0; i < allowed_no; i++) { + if (chan->chan == allowed[i]) + return 1; + } + + return 0; +} + + +static int dfs_chan_range_available(struct hostapd_hw_modes *mode, + int first_chan_idx, int num_chans, + int skip_radar) +{ + struct hostapd_channel_data *first_chan, *chan; + int i; + + if (first_chan_idx + num_chans >= mode->num_channels) + return 0; + + first_chan = &mode->channels[first_chan_idx]; + + for (i = 0; i < num_chans; i++) { + chan = &mode->channels[first_chan_idx + i]; + + if (first_chan->freq + i * 20 != chan->freq) + return 0; + + if (!dfs_channel_available(chan, skip_radar)) + return 0; + } + + return 1; +} + + +static int is_in_chanlist(struct hostapd_iface *iface, + struct hostapd_channel_data *chan) +{ + int *entry; + + if (!iface->conf->chanlist) + return 1; + + for (entry = iface->conf->chanlist; *entry != -1; entry++) { + if (*entry == chan->chan) + return 1; + } + return 0; +} + + +/* + * The function assumes HT40+ operation. + * Make sure to adjust the following variables after calling this: + * - hapd->secondary_channel + * - hapd->vht_oper_centr_freq_seg0_idx + * - hapd->vht_oper_centr_freq_seg1_idx + */ +static int dfs_find_channel(struct hostapd_iface *iface, + struct hostapd_channel_data **ret_chan, + int idx, int skip_radar) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int i, channel_idx = 0, n_chans, n_chans1; + + mode = iface->current_mode; + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + /* Skip HT40/VHT incompatible channels */ + if (iface->conf->ieee80211n && + iface->conf->secondary_channel && + !dfs_is_chan_allowed(chan, n_chans)) + continue; + + /* Skip incompatible chandefs */ + if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) + continue; + + if (!is_in_chanlist(iface, chan)) + continue; + + if (ret_chan && idx == channel_idx) { + wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); + *ret_chan = chan; + return idx; + } + wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); + channel_idx++; + } + return channel_idx; +} + + +static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + int secondary_channel, + u8 *vht_oper_centr_freq_seg0_idx, + u8 *vht_oper_centr_freq_seg1_idx) +{ + if (!iface->conf->ieee80211ac) + return; + + if (!chan) + return; + + *vht_oper_centr_freq_seg1_idx = 0; + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (secondary_channel == 1) + *vht_oper_centr_freq_seg0_idx = chan->chan + 2; + else if (secondary_channel == -1) + *vht_oper_centr_freq_seg0_idx = chan->chan - 2; + else + *vht_oper_centr_freq_seg0_idx = chan->chan; + break; + case VHT_CHANWIDTH_80MHZ: + *vht_oper_centr_freq_seg0_idx = chan->chan + 6; + break; + case VHT_CHANWIDTH_160MHZ: + *vht_oper_centr_freq_seg0_idx = chan->chan + 14; + break; + default: + wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); + *vht_oper_centr_freq_seg0_idx = 0; + break; + } + + wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", + *vht_oper_centr_freq_seg0_idx, + *vht_oper_centr_freq_seg1_idx); +} + + +/* Return start channel idx we will use for mode->channels[idx] */ +static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int channel_no = iface->conf->channel; + int res = -1, i; + int chan_seg1 = -1; + + *seg1_start = -1; + + /* HT40- */ + if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) + channel_no -= 4; + + /* VHT */ + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 6; + break; + case VHT_CHANWIDTH_160MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 14; + break; + case VHT_CHANWIDTH_80P80MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 6; + chan_seg1 = + iface->conf->vht_oper_centr_freq_seg1_idx - 6; + break; + default: + wpa_printf(MSG_INFO, + "DFS only VHT20/40/80/160/80+80 is supported now"); + channel_no = -1; + break; + } + } + + /* Get idx */ + mode = iface->current_mode; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == channel_no) { + res = i; + break; + } + } + + if (res != -1 && chan_seg1 > -1) { + int found = 0; + + /* Get idx for seg1 */ + mode = iface->current_mode; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == chan_seg1) { + *seg1_start = i; + found = 1; + break; + } + } + if (!found) + res = -1; + } + + if (res == -1) { + wpa_printf(MSG_DEBUG, + "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d", + mode->num_channels, channel_no, iface->conf->channel, + iface->conf->ieee80211n, + iface->conf->secondary_channel, + iface->conf->vht_oper_chwidth); + + for (i = 0; i < mode->num_channels; i++) { + wpa_printf(MSG_DEBUG, "Available channel: %d", + mode->channels[i].chan); + } + } + + return res; +} + + +/* At least one channel have radar flag */ +static int dfs_check_chans_radar(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_RADAR) + res++; + } + + return res; +} + + +/* All channels available */ +static int dfs_check_chans_available(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i; + + mode = iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + + if (channel->flag & HOSTAPD_CHAN_DISABLED) + break; + + if (!(channel->flag & HOSTAPD_CHAN_RADAR)) + continue; + + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != + HOSTAPD_CHAN_DFS_AVAILABLE) + break; + } + + return i == n_chans; +} + + +/* At least one channel unavailable */ +static int dfs_check_chans_unavailable(struct hostapd_iface *iface, + int start_chan_idx, + int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_DISABLED) + res++; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE) + res++; + } + + return res; +} + + +static struct hostapd_channel_data * +dfs_get_valid_channel(struct hostapd_iface *iface, + int *secondary_channel, + u8 *vht_oper_centr_freq_seg0_idx, + u8 *vht_oper_centr_freq_seg1_idx, + int skip_radar) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int num_available_chandefs; + int chan_idx; + u32 _rand; + + wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); + *secondary_channel = 0; + *vht_oper_centr_freq_seg0_idx = 0; + *vht_oper_centr_freq_seg1_idx = 0; + + if (iface->current_mode == NULL) + return NULL; + + mode = iface->current_mode; + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return NULL; + + /* Get the count first */ + num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); + if (num_available_chandefs == 0) + return NULL; + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); + chan_idx = _rand % num_available_chandefs; + dfs_find_channel(iface, &chan, chan_idx, skip_radar); + + /* dfs_find_channel() calculations assume HT40+ */ + if (iface->conf->secondary_channel) + *secondary_channel = 1; + else + *secondary_channel = 0; + + dfs_adjust_vht_center_freq(iface, chan, + *secondary_channel, + vht_oper_centr_freq_seg0_idx, + vht_oper_centr_freq_seg1_idx); + + return chan; +} + + +static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int i; + + mode = iface->current_mode; + if (mode == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->freq == freq) { + if (chan->flag & HOSTAPD_CHAN_RADAR) { + chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; + chan->flag |= state; + return 1; /* Channel found */ + } + } + } + wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); + return 0; +} + + +static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, + int chan_offset, int chan_width, int cf1, + int cf2, u32 state) +{ + int n_chans = 1, i; + struct hostapd_hw_modes *mode; + int frequency = freq; + int ret = 0; + + mode = iface->current_mode; + if (mode == NULL) + return 0; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) { + wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); + return 0; + } + + /* Seems cf1 and chan_width is enough here */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + n_chans = 1; + if (frequency == 0) + frequency = cf1; + break; + case CHAN_WIDTH_40: + n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, + n_chans); + for (i = 0; i < n_chans; i++) { + ret += set_dfs_state_freq(iface, frequency, state); + frequency = frequency + 20; + } + + return ret; +} + + +static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, + int chan_width, int cf1, int cf2) +{ + int start_chan_idx, start_chan_idx1; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1; + u8 radar_chan; + int res = 0; + + /* Our configuration */ + mode = iface->current_mode; + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + /* Check we are on DFS channel(s) */ + if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) + return 0; + + /* Reported via radar event */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + radar_n_chans = 1; + if (frequency == 0) + frequency = cf1; + break; + case CHAN_WIDTH_40: + radar_n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + radar_n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + radar_n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + ieee80211_freq_to_chan(frequency, &radar_chan); + + for (i = 0; i < n_chans; i++) { + chan = &mode->channels[start_chan_idx + i]; + if (!(chan->flag & HOSTAPD_CHAN_RADAR)) + continue; + for (j = 0; j < radar_n_chans; j++) { + wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", + chan->chan, radar_chan + j * 4); + if (chan->chan == radar_chan + j * 4) + res++; + } + } + + wpa_printf(MSG_DEBUG, "overlapped: %d", res); + + return res; +} + + +static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i; + unsigned int cac_time_ms = 0; + + mode = iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (!(channel->flag & HOSTAPD_CHAN_RADAR)) + continue; + if (channel->dfs_cac_ms > cac_time_ms) + cac_time_ms = channel->dfs_cac_ms; + } + + return cac_time_ms; +} + + +/* + * Main DFS handler + * 1 - continue channel/ap setup + * 0 - channel/ap setup will be continued after CAC + * -1 - hit critical error + */ +int hostapd_handle_dfs(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; + int skip_radar = 0; + + if (!iface->current_mode) { + /* + * This can happen with drivers that do not provide mode + * information and as such, cannot really use hostapd for DFS. + */ + wpa_printf(MSG_DEBUG, + "DFS: No current_mode information - assume no need to perform DFS operations by hostapd"); + return 1; + } + + iface->cac_started = 0; + + do { + /* Get start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface, + &start_chan_idx1); + if (start_chan_idx == -1) + return -1; + + /* Get number of used channels, depend on width */ + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + /* Setup CAC time */ + iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx, + n_chans); + + /* Check if any of configured channels require DFS */ + res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS %d channels required radar detection", + res); + if (!res) + return 1; + + /* Check if all channels are DFS available */ + res = dfs_check_chans_available(iface, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS all channels available, (SKIP CAC): %s", + res ? "yes" : "no"); + if (res) + return 1; + + /* Check if any of configured channels is unavailable */ + res = dfs_check_chans_unavailable(iface, start_chan_idx, + n_chans); + wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", + res, res ? "yes": "no"); + if (res) { + int sec = 0; + u8 cf1 = 0, cf2 = 0; + + channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, + skip_radar); + if (!channel) { + wpa_printf(MSG_ERROR, "could not get valid channel"); + return -1; + } + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = sec; + iface->conf->vht_oper_centr_freq_seg0_idx = cf1; + iface->conf->vht_oper_centr_freq_seg1_idx = cf2; + } + } while (res); + + /* Finally start CAC */ + hostapd_set_state(iface, HAPD_IFACE_DFS); + wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START + "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", + iface->freq, + iface->conf->channel, iface->conf->secondary_channel, + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx, + iface->dfs_cac_ms / 1000); + + res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode, + iface->freq, + iface->conf->channel, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + iface->conf->secondary_channel, + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx); + + if (res) { + wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); + return -1; + } + + return 0; +} + + +int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED + "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + if (success) { + /* Complete iface/ap configuration */ + if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) { + /* Complete AP configuration for the first bring up. */ + if (iface->state != HAPD_IFACE_ENABLED) + hostapd_setup_interface_complete(iface, 0); + else + iface->cac_started = 0; + } else { + set_dfs_state(iface, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_AVAILABLE); + iface->cac_started = 0; + hostapd_setup_interface_complete(iface, 0); + } + } + + return 0; +} + + +static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx = 0; + u8 vht_oper_centr_freq_seg1_idx = 0; + int skip_radar = 0; + int err = 1; + + /* Radar detected during active CAC */ + iface->cac_started = 0; + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + + if (!channel) { + wpa_printf(MSG_ERROR, "No valid channel available"); + hostapd_setup_interface_complete(iface, err); + return err; + } + + wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", + channel->chan); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL + "freq=%d chan=%d sec_chan=%d", channel->freq, + channel->chan, secondary_channel); + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + err = 0; + + hostapd_setup_interface_complete(iface, err); + return err; +} + + +static int hostapd_csa_in_progress(struct hostapd_iface *iface) +{ + unsigned int i; + for (i = 0; i < iface->num_bss; i++) + if (iface->bss[i]->csa_in_progress) + return 1; + return 0; +} + + +static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + int skip_radar = 1; + struct csa_settings csa_settings; + unsigned int i; + int err = 1; + + wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", + __func__, iface->cac_started ? "yes" : "no", + hostapd_csa_in_progress(iface) ? "yes" : "no"); + + /* Check if CSA in progress */ + if (hostapd_csa_in_progress(iface)) + return 0; + + /* Check if active CAC */ + if (iface->cac_started) + return hostapd_dfs_start_channel_switch_cac(iface); + + /* Perform channel switch/CSA */ + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + + if (!channel) { + /* + * If there is no channel to switch immediately to, check if + * there is another channel where we can switch even if it + * requires to perform a CAC first. + */ + skip_radar = 0; + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + if (!channel) { + /* FIXME: Wait for channel(s) to become available */ + hostapd_disable_iface(iface); + return err; + } + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + return 0; + } + + wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", + channel->chan); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL + "freq=%d chan=%d sec_chan=%d", channel->freq, + channel->chan, secondary_channel); + + /* Setup CSA request */ + os_memset(&csa_settings, 0, sizeof(csa_settings)); + csa_settings.cs_count = 5; + csa_settings.block_tx = 1; + err = hostapd_set_freq_params(&csa_settings.freq_params, + iface->conf->hw_mode, + channel->freq, + channel->chan, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + secondary_channel, + iface->conf->vht_oper_chwidth, + vht_oper_centr_freq_seg0_idx, + vht_oper_centr_freq_seg1_idx, + iface->current_mode->vht_capab); + + if (err) { + wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); + hostapd_disable_iface(iface); + return err; + } + + for (i = 0; i < iface->num_bss; i++) { + err = hostapd_switch_channel(iface->bss[i], &csa_settings); + if (err) + break; + } + + if (err) { + wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", + err); + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + return 0; + } + + /* Channel configuration will be updated once CSA completes and + * ch_switch_notify event is received */ + + wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); + return 0; +} + + +int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + int res; + + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* Proceed only if DFS is not offloaded to the driver */ + if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return 0; + + if (!iface->conf->ieee80211h) + return 0; + + /* mark radar frequency as invalid */ + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE); + + /* Skip if reported radar event not overlapped our channels */ + res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); + if (!res) + return 0; + + /* radar detected while operating, switch the channel. */ + res = hostapd_dfs_start_channel_switch(iface); + + return res; +} + + +int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* Proceed only if DFS is not offloaded to the driver */ + if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return 0; + + /* TODO add correct implementation here */ + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + return 0; +} + + +int hostapd_is_dfs_required(struct hostapd_iface *iface) +{ + int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res; + + if (!iface->conf->ieee80211h || !iface->current_mode || + iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + /* Get start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); + if (start_chan_idx == -1) + return -1; + + /* Get number of used channels, depend on width */ + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + /* Check if any of configured channels require DFS */ + res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); + if (res) + return res; + if (start_chan_idx1 >= 0 && n_chans1 > 0) + res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1); + return res; +} + + +int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START + "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " + "seg1=%d cac_time=%ds", + freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60); + iface->cac_started = 1; + return 0; +} + + +/* + * Main DFS handler for offloaded case. + * 2 - continue channel/AP setup for non-DFS channel + * 1 - continue channel/AP setup for DFS channel + * 0 - channel/AP setup will be continued after CAC + * -1 - hit critical error + */ +int hostapd_handle_dfs_offload(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", + __func__, iface->cac_started); + + /* + * If DFS has already been started, then we are being called from a + * callback to continue AP/channel setup. Reset the CAC start flag and + * return. + */ + if (iface->cac_started) { + wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", + __func__, iface->cac_started); + iface->cac_started = 0; + return 1; + } + + if (ieee80211_is_dfs(iface->freq)) { + wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS", + __func__, iface->freq); + return 0; + } + + wpa_printf(MSG_DEBUG, + "%s: freq %d MHz does not require DFS. Continue channel/AP setup", + __func__, iface->freq); + return 2; +} diff --git a/src/ap/dfs.h b/src/ap/dfs.h new file mode 100644 index 000000000000..be8c0e6001c9 --- /dev/null +++ b/src/ap/dfs.h @@ -0,0 +1,30 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef DFS_H +#define DFS_H + +int hostapd_handle_dfs(struct hostapd_iface *iface); + +int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, int cf1, int cf2); +int hostapd_is_dfs_required(struct hostapd_iface *iface); +int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_handle_dfs_offload(struct hostapd_iface *iface); + +#endif /* DFS_H */ diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c new file mode 100644 index 000000000000..3a77225f380e --- /dev/null +++ b/src/ap/dhcp_snoop.c @@ -0,0 +1,178 @@ +/* + * DHCP snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "x_snoop.h" +#include "dhcp_snoop.h" + +struct bootp_pkt { + struct iphdr iph; + struct udphdr udph; + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; + u8 exten[312]; +} STRUCT_PACKED; + +#define DHCPACK 5 +static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; + + +static const char * ipaddr_str(u32 addr) +{ + static char buf[17]; + + os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + return buf; +} + + +static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + const struct bootp_pkt *b; + struct sta_info *sta; + int exten_len; + const u8 *end, *pos; + int res, msgtype = 0, prefixlen = 32; + u32 subnet_mask = 0; + u16 tot_len; + + exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten)); + if (exten_len < 4) + return; + + b = (const struct bootp_pkt *) &buf[ETH_HLEN]; + tot_len = ntohs(b->iph.tot_len); + if (tot_len > (unsigned int) (len - ETH_HLEN)) + return; + + if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) + return; + + /* Parse DHCP options */ + end = (const u8 *) b + tot_len; + pos = &b->exten[4]; + while (pos < end && *pos != 0xff) { + const u8 *opt = pos++; + + if (*opt == 0) /* padding */ + continue; + + pos += *pos + 1; + if (pos >= end) + break; + + switch (*opt) { + case 1: /* subnet mask */ + if (opt[1] == 4) + subnet_mask = WPA_GET_BE32(&opt[2]); + if (subnet_mask == 0) + return; + while (!(subnet_mask & 0x1)) { + subnet_mask >>= 1; + prefixlen--; + } + break; + case 53: /* message type */ + if (opt[1]) + msgtype = opt[2]; + break; + default: + break; + } + } + + if (msgtype == DHCPACK) { + if (b->your_ip == 0) + return; + + /* DHCPACK for DHCPREQUEST */ + sta = ap_get_sta(hapd, b->hw_addr); + if (!sta) + return; + + wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR + " @ IPv4 address %s/%d", + MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)), + prefixlen); + + if (sta->ipaddr == b->your_ip) + return; + + if (sta->ipaddr != 0) { + wpa_printf(MSG_DEBUG, + "dhcp_snoop: Removing IPv4 address %s from the ip neigh table", + ipaddr_str(be_to_host32(sta->ipaddr))); + hostapd_drv_br_delete_ip_neigh(hapd, 4, + (u8 *) &sta->ipaddr); + } + + res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip, + prefixlen, sta->addr); + if (res) { + wpa_printf(MSG_DEBUG, + "dhcp_snoop: Adding ip neigh table failed: %d", + res); + return; + } + sta->ipaddr = b->your_ip; + } + + if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, + (u8 *) buf, len); + } + } +} + + +int dhcp_snoop_init(struct hostapd_data *hapd) +{ + hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp, + L2_PACKET_FILTER_DHCP); + if (hapd->sock_dhcp == NULL) { + wpa_printf(MSG_DEBUG, + "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void dhcp_snoop_deinit(struct hostapd_data *hapd) +{ + l2_packet_deinit(hapd->sock_dhcp); +} diff --git a/src/ap/dhcp_snoop.h b/src/ap/dhcp_snoop.h new file mode 100644 index 000000000000..93d0050fac24 --- /dev/null +++ b/src/ap/dhcp_snoop.h @@ -0,0 +1,30 @@ +/* + * DHCP snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DHCP_SNOOP_H +#define DHCP_SNOOP_H + +#ifdef CONFIG_PROXYARP + +int dhcp_snoop_init(struct hostapd_data *hapd); +void dhcp_snoop_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_PROXYARP */ + +static inline int dhcp_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void dhcp_snoop_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_PROXYARP */ + +#endif /* DHCP_SNOOP_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 8980bec03f8b..a0adc67db430 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -1,6 +1,6 @@ /* * hostapd / Callback functions for driver wrappers - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,10 +9,12 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "radius/radius.h" #include "drivers/driver.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" @@ -28,6 +30,8 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "hw_features.h" +#include "dfs.h" +#include "beacon.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -44,6 +48,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #endif /* CONFIG_IEEE80211R */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; + const u8 *p2p_dev_addr = NULL; if (addr == NULL) { /* @@ -75,6 +80,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, ie = elems.wpa_ie - 2; ielen = elems.wpa_ie_len + 2; wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq"); +#ifdef CONFIG_HS20 + } else if (elems.osen) { + ie = elems.osen - 2; + ielen = elems.osen_len + 2; + wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq"); +#endif /* CONFIG_HS20 */ } else { ie = NULL; ielen = 0; @@ -84,6 +95,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta = ap_get_sta(hapd, addr); if (sta) { + ap_sta_no_session_timeout(hapd, sta); accounting_sta_stop(hapd, sta); /* @@ -106,9 +118,36 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, wpabuf_free(sta->p2p_ie); sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, P2P_IE_VENDOR_TYPE); + if (sta->p2p_ie) + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); } #endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + if (elems.ht_capabilities && + elems.ht_capabilities_len >= + sizeof(struct ieee80211_ht_capabilities) && + (hapd->iface->conf->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + 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) + ht40_intolerant_add(hapd->iface, sta); + } +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_INTERWORKING + if (elems.ext_capab && elems.ext_capab_len > 4) { + if (elems.ext_capab[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { @@ -154,7 +193,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, + p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPA state " "machine"); @@ -267,6 +307,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->flags |= WLAN_STA_MAYBE_WPS; wpabuf_free(wps); #endif /* CONFIG_WPS */ +#ifdef CONFIG_HS20 + } else if (hapd->conf->osen) { + if (elems.osen == NULL) { + hostapd_logger( + hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "No HS 2.0 OSEN element in association request"); + return WLAN_STATUS_INVALID_IE; + } + + wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association"); + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize WPA " + "state machine"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, + elems.osen - 2, elems.osen_len + 2) < 0) + return WLAN_STATUS_INVALID_IE; +#endif /* CONFIG_HS20 */ } #ifdef CONFIG_WPS skip_wpa_check: @@ -277,6 +340,9 @@ skip_wpa_check: sta->auth_alg, req_ies, req_ies_len); hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); + + if (sta->auth_alg == WLAN_AUTH_FT) + ap_sta_set_authorized(hapd, sta, 1); #else /* CONFIG_IEEE80211R */ /* Keep compiler silent about unused variables */ if (status) { @@ -285,6 +351,9 @@ skip_wpa_check: new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + + hostapd_set_sta_flags(hapd, sta); if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); @@ -367,14 +436,16 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, - int offset) + int offset, int width, int cf1, int cf2) { #ifdef NEED_AP_MLME - int channel; + int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "driver had channel switch: " - "freq=%d, ht=%d, offset=%d", freq, ht, offset); + HOSTAPD_LEVEL_INFO, + "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + freq, ht, offset, width, channel_width_to_string(width), + cf1, cf2); hapd->iface->freq = freq; @@ -386,13 +457,124 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, return; } + switch (width) { + case CHAN_WIDTH_80: + chwidth = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + chwidth = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + chwidth = VHT_CHANWIDTH_160MHZ; + break; + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + default: + chwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + if (cf1 > 5000) + seg0_idx = (cf1 - 5000) / 5; + if (cf2 > 5000) + seg1_idx = (cf2 - 5000) / 5; + break; + default: + seg0_idx = hostapd_hw_get_channel(hapd, cf1); + seg1_idx = hostapd_hw_get_channel(hapd, cf2); + break; + } + hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; + if (!ht) + hapd->iconf->ieee80211ac = 0; hapd->iconf->secondary_channel = offset; + hapd->iconf->vht_oper_chwidth = chwidth; + hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; + hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; + + is_dfs = ieee80211_is_dfs(freq); + + if (hapd->csa_in_progress && + freq == hapd->cs_freq_params.freq) { + hostapd_cleanup_cs_params(hapd); + ieee802_11_set_beacon(hapd); + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED + "freq=%d dfs=%d", freq, is_dfs); + } else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) { + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED + "freq=%d dfs=%d", freq, is_dfs); + } #endif /* NEED_AP_MLME */ } +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, + const u8 *addr, int reason_code) +{ + switch (reason_code) { + case MAX_CLIENT_REACHED: + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR, + MAC2STR(addr)); + break; + case BLOCKED_CLIENT: + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR, + MAC2STR(addr)); + break; + } +} + + +#ifdef CONFIG_ACS +static void hostapd_acs_channel_selected(struct hostapd_data *hapd, + u8 pri_channel, u8 sec_channel) +{ + int channel; + int ret; + + if (hapd->iconf->channel) { + wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", + hapd->iconf->channel); + return; + } + + hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel); + + channel = pri_channel; + if (!channel) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver switched to bad channel"); + return; + } + + hapd->iconf->channel = channel; + + if (sec_channel == 0) + hapd->iconf->secondary_channel = 0; + else if (sec_channel < pri_channel) + hapd->iconf->secondary_channel = -1; + else if (sec_channel > pri_channel) + hapd->iconf->secondary_channel = 1; + else { + wpa_printf(MSG_ERROR, "Invalid secondary channel!"); + return; + } + + ret = hostapd_acs_completed(hapd->iface, 0); + if (ret) { + wpa_printf(MSG_ERROR, + "ACS: Possibly channel configuration is invalid"); + } +} +#endif /* CONFIG_ACS */ + + 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) @@ -452,7 +634,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, if (!sta) { sta = ap_sta_add(hapd, rx_auth->peer); if (sta == NULL) { - status = WLAN_STATUS_UNSPECIFIED_FAILURE; + status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } } @@ -463,7 +645,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); @@ -484,39 +666,48 @@ fail: static void hostapd_action_rx(struct hostapd_data *hapd, - struct rx_action *action) + struct rx_mgmt *drv_mgmt) { + struct ieee80211_mgmt *mgmt; struct sta_info *sta; + size_t plen __maybe_unused; + u16 fc; + + if (drv_mgmt->frame_len < 24 + 1) + return; + + plen = drv_mgmt->frame_len - 24 - 1; + + mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) + return; /* handled by the driver */ wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", - action->category, (int) action->len); + mgmt->u.action.category, (int) plen); - sta = ap_get_sta(hapd, action->sa); + sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return; } #ifdef CONFIG_IEEE80211R - if (action->category == WLAN_ACTION_FT) { - wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d", - __func__, (int) action->len); - wpa_ft_action_rx(sta->wpa_sm, action->data, action->len); + if (mgmt->u.action.category == WLAN_ACTION_FT) { + const u8 *payload = drv_mgmt->frame + 24 + 1; + wpa_ft_action_rx(sta->wpa_sm, payload, plen); } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W - if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) { - wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d", - __func__, (int) action->len); - ieee802_11_sa_query_action(hapd, action->sa, - *(action->data + 1), - action->data + 2); + if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { + ieee802_11_sa_query_action( + hapd, mgmt->sa, + mgmt->u.action.u.sa_query_resp.action, + mgmt->u.action.u.sa_query_resp.trans_id); } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM - if (action->category == WLAN_ACTION_WNM) { - wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d", - __func__, (int) action->len); - ieee802_11_rx_wnm_action_ap(hapd, action); + if (mgmt->u.action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } #endif /* CONFIG_WNM */ } @@ -558,17 +749,32 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd, } -static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) +static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) { struct hostapd_iface *iface = hapd->iface; const struct ieee80211_hdr *hdr; const u8 *bssid; struct hostapd_frame_info fi; + int ret; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_mgmt_frame_handling) { + size_t hex_len = 2 * rx_mgmt->frame_len + 1; + char *hex = os_malloc(hex_len); + if (hex) { + wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame, + rx_mgmt->frame_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex); + os_free(hex); + } + return 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); if (bssid == NULL) - return; + return 0; hapd = get_hapd_bssid(iface, bssid); if (hapd == NULL) { @@ -583,7 +789,7 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) hapd = iface->bss[0]; else - return; + return 0; } os_memset(&fi, 0, sizeof(fi)); @@ -592,53 +798,25 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) if (hapd == HAPD_BROADCAST) { size_t i; - for (i = 0; i < iface->num_bss; i++) - ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, - rx_mgmt->frame_len, &fi); + ret = 0; + for (i = 0; i < iface->num_bss; i++) { + /* if bss is set, driver will call this function for + * each bss individually. */ + if (rx_mgmt->drv_priv && + (iface->bss[i]->drv_priv != rx_mgmt->drv_priv)) + continue; + + if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, + rx_mgmt->frame_len, &fi) > 0) + ret = 1; + } } else - ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); + ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, + &fi); random_add_randomness(&fi, sizeof(fi)); -} - -static void hostapd_rx_action(struct hostapd_data *hapd, - struct rx_action *rx_action) -{ - struct rx_mgmt rx_mgmt; - u8 *buf; - struct ieee80211_hdr *hdr; - - wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR - " BSSID=" MACSTR " category=%u", - MAC2STR(rx_action->da), MAC2STR(rx_action->sa), - MAC2STR(rx_action->bssid), rx_action->category); - wpa_hexdump(MSG_MSGDUMP, "Received action frame contents", - rx_action->data, rx_action->len); - - buf = os_zalloc(24 + 1 + rx_action->len); - if (buf == NULL) - return; - hdr = (struct ieee80211_hdr *) buf; - hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - if (rx_action->category == WLAN_ACTION_SA_QUERY) { - /* - * Assume frame was protected; it would have been dropped if - * not. - */ - hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); - } - os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN); - os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN); - os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN); - buf[24] = rx_action->category; - os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len); - os_memset(&rx_mgmt, 0, sizeof(rx_mgmt)); - rx_mgmt.frame = buf; - rx_mgmt.frame_len = 24 + 1 + rx_action->len; - hostapd_mgmt_rx(hapd, &rx_mgmt); - os_free(buf); + return ret; } @@ -697,6 +875,181 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, } +static struct hostapd_channel_data * hostapd_get_mode_channel( + struct hostapd_iface *iface, unsigned int freq) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (!chan) + return NULL; + if ((unsigned int) chan->freq == freq) + return chan; + } + + return NULL; +} + + +static void hostapd_update_nf(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + struct freq_survey *survey) +{ + if (!iface->chans_surveyed) { + chan->min_nf = survey->nf; + iface->lowest_nf = survey->nf; + } else { + if (dl_list_empty(&chan->survey_list)) + chan->min_nf = survey->nf; + else if (survey->nf < chan->min_nf) + chan->min_nf = survey->nf; + if (survey->nf < iface->lowest_nf) + iface->lowest_nf = survey->nf; + } +} + + +static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_res) +{ + struct hostapd_channel_data *chan; + struct freq_survey *survey; + u64 divisor, dividend; + + survey = dl_list_first(&survey_res->survey_list, struct freq_survey, + list); + if (!survey || !survey->freq) + return; + + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) + return; + + wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", + survey->freq, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy); + + if (survey->channel_time > iface->last_channel_time && + survey->channel_time > survey->channel_time_busy) { + dividend = survey->channel_time_busy - + iface->last_channel_time_busy; + divisor = survey->channel_time - iface->last_channel_time; + + iface->channel_utilization = dividend * 255 / divisor; + wpa_printf(MSG_DEBUG, "Channel Utilization: %d", + iface->channel_utilization); + } + iface->last_channel_time = survey->channel_time; + iface->last_channel_time_busy = survey->channel_time_busy; +} + + +static void hostapd_event_get_survey(struct hostapd_data *hapd, + struct survey_results *survey_results) +{ + struct hostapd_iface *iface = hapd->iface; + struct freq_survey *survey, *tmp; + struct hostapd_channel_data *chan; + + if (dl_list_empty(&survey_results->survey_list)) { + wpa_printf(MSG_DEBUG, "No survey data received"); + return; + } + + if (survey_results->freq_filter) { + hostapd_single_channel_get_survey(iface, survey_results); + return; + } + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan) + continue; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + dl_list_del(&survey->list); + dl_list_add_tail(&chan->survey_list, &survey->list); + + hostapd_update_nf(iface, chan, survey); + + iface->chans_surveyed++; + } +} + + +#ifdef NEED_AP_MLME + +static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped", + hapd->conf->iface); + + if (hapd->csa_in_progress) { + wpa_printf(MSG_INFO, "CSA failed (%s was stopped)", + hapd->conf->iface); + hostapd_switch_channel_fallback(hapd->iface, + &hapd->cs_freq_params); + } +} + + +static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); + hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); + hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq); + hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + +#endif /* NEED_AP_MLME */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -713,6 +1066,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) level = MSG_EXCESSIVE; + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) + level = MSG_EXCESSIVE; } wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received", @@ -727,12 +1083,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (hapd->iface->scan_cb) hapd->iface->scan_cb(hapd->iface); break; -#ifdef CONFIG_IEEE80211R - case EVENT_FT_RRB_RX: - wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src, - data->ft_rrb_rx.data, data->ft_rrb_rx.data_len); - break; -#endif /* CONFIG_IEEE80211R */ case EVENT_WPS_BUTTON_PUSHED: hostapd_wps_button_pushed(hapd, NULL); break; @@ -767,10 +1117,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; +#endif /* NEED_AP_MLME */ case EVENT_RX_MGMT: - hostapd_mgmt_rx(hapd, &data->rx_mgmt); - break; + if (!data->rx_mgmt.frame) + break; +#ifdef NEED_AP_MLME + if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) + break; #endif /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_mgmt); + break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) @@ -791,6 +1147,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->eapol_rx.data_len); break; case EVENT_ASSOC: + if (!data) + return; hostapd_notif_assoc(hapd, data->assoc_info.addr, data->assoc_info.req_ies, data->assoc_info.req_ies_len, @@ -809,15 +1167,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; hostapd_event_sta_low_ack(hapd, data->low_ack.addr); break; - case EVENT_RX_ACTION: - if (data->rx_action.da == NULL || data->rx_action.sa == NULL || - data->rx_action.bssid == NULL) - break; -#ifdef NEED_AP_MLME - hostapd_rx_action(hapd, &data->rx_action); -#endif /* NEED_AP_MLME */ - hostapd_action_rx(hapd, &data->rx_action); - break; case EVENT_AUTH: hostapd_notif_auth(hapd, &data->auth); break; @@ -826,8 +1175,84 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; hostapd_event_ch_switch(hapd, data->ch_switch.freq, data->ch_switch.ht_enabled, - data->ch_switch.ch_offset); + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); + break; + case EVENT_CONNECT_FAILED_REASON: + if (!data) + break; + hostapd_event_connect_failed_reason( + hapd, data->connect_failed_reason.addr, + data->connect_failed_reason.code); + break; + case EVENT_SURVEY: + hostapd_event_get_survey(hapd, &data->survey_results); + break; +#ifdef NEED_AP_MLME + case EVENT_INTERFACE_UNAVAILABLE: + hostapd_event_iface_unavailable(hapd); + break; + case EVENT_DFS_RADAR_DETECTED: + if (!data) + break; + hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_FINISHED: + if (!data) + break; + hostapd_event_dfs_cac_finished(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_ABORTED: + if (!data) + break; + hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event); + break; + case EVENT_DFS_NOP_FINISHED: + if (!data) + break; + hostapd_event_dfs_nop_finished(hapd, &data->dfs_event); + break; + case EVENT_CHANNEL_LIST_CHANGED: + /* channel list changed (regulatory?), update channel list */ + /* TODO: check this. hostapd_get_hw_features() initializes + * too much stuff. */ + /* hostapd_get_hw_features(hapd->iface); */ + hostapd_channel_list_updated( + hapd->iface, data->channel_list_changed.initiator); + break; + case EVENT_DFS_CAC_STARTED: + if (!data) + break; + hostapd_event_dfs_cac_started(hapd, &data->dfs_event); + break; +#endif /* NEED_AP_MLME */ + case EVENT_INTERFACE_ENABLED: + wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED); + if (hapd->disabled && hapd->started) { + hapd->disabled = 0; + /* + * Try to re-enable interface if the driver stopped it + * when the interface got disabled. + */ + wpa_auth_reconfig_group_keys(hapd->wpa_auth); + hapd->reenable_beacon = 1; + ieee802_11_set_beacon(hapd); + } + break; + case EVENT_INTERFACE_DISABLED: + hostapd_free_stas(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED); + hapd->disabled = 1; + break; +#ifdef CONFIG_ACS + case EVENT_ACS_CHANNEL_SELECTED: + hostapd_acs_channel_selected( + hapd, data->acs_selected_channels.pri_channel, + data->acs_selected_channels.sec_channel); break; +#endif /* CONFIG_ACS */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 79d50e51694e..559d77f9ef2b 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -83,12 +83,14 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) for (i = 0; i < argc; i++) { if (os_strcmp(col[i], "password") == 0 && argv[i]) { - os_free(user->password); + bin_clear_free(user->password, user->password_len); user->password_len = os_strlen(argv[i]); user->password = (u8 *) os_strdup(argv[i]); user->next = (void *) 1; } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { set_user_methods(user, argv[i]); + } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { + user->remediation = strlen(argv[i]) > 0; } } @@ -116,7 +118,7 @@ static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) if (len <= user->identity_len && os_memcmp(argv[id], user->identity, len) == 0 && (user->password == NULL || len > user->password_len)) { - os_free(user->password); + bin_clear_free(user->password, user->password_len); user->password_len = os_strlen(argv[id]); user->password = (u8 *) os_strdup(argv[id]); user->next = (void *) 1; @@ -156,8 +158,10 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, return NULL; } - os_free(hapd->tmp_eap_user.identity); - os_free(hapd->tmp_eap_user.password); + bin_clear_free(hapd->tmp_eap_user.identity, + hapd->tmp_eap_user.identity_len); + bin_clear_free(hapd->tmp_eap_user.password, + hapd->tmp_eap_user.password_len); os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); hapd->tmp_eap_user.phase2 = phase2; hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); @@ -173,8 +177,8 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, } os_snprintf(cmd, sizeof(cmd), - "SELECT password,methods FROM users WHERE " - "identity='%s' AND phase2=%d;", id_str, phase2); + "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", + id_str, phase2); wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 851c183e97f8..9d19f98d0b7c 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -1,6 +1,6 @@ /* * Generic advertisement service (GAS) server - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,13 @@ #include "gas_serv.h" +static void convert_to_protected_dual(struct wpabuf *msg) +{ + u8 *categ = wpabuf_mhead_u8(msg); + *categ = WLAN_ACTION_PROTECTED_DUAL; +} + + static struct gas_dialog_info * gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) { @@ -46,10 +53,12 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) * it to be that long. */ ap_sta_session_timeout(hapd, sta, 5); + } else { + ap_sta_replenish_timeout(hapd, sta, 5); } if (sta->gas_dialog == NULL) { - sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * + sta->gas_dialog = os_calloc(GAS_DIALOG_MAX, sizeof(struct gas_dialog_info)); if (sta->gas_dialog == NULL) return NULL; @@ -62,7 +71,6 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) continue; dia = &sta->gas_dialog[i]; dia->valid = 1; - dia->index = i; dia->dialog_token = dialog_token; sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; return dia; @@ -150,6 +158,10 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); if (hapd->conf->hs20_operating_class) wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + if (hapd->conf->hs20_osu_providers_count) + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); + if (hapd->conf->hs20_icons_count) + wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ @@ -402,7 +414,7 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, gas_anqp_set_element_len(buf, realm_data_len); } gas_anqp_set_element_len(buf, len); - } else if (nai_home_realm && hapd->conf->nai_realm_data) { + } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) { hs20_add_nai_home_realm_matches(hapd, buf, home_realm, home_realm_len); } @@ -505,18 +517,188 @@ static void anqp_add_operating_class(struct hostapd_data *hapd, } } + +static void anqp_add_osu_provider(struct wpabuf *buf, + struct hostapd_bss_config *bss, + struct hs20_osu_provider *p) +{ + u8 *len, *len2, *count; + unsigned int i; + + len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */ + + /* OSU Friendly Name Duples */ + len2 = wpabuf_put(buf, 2); + for (i = 0; i < p->friendly_name_count; i++) { + struct hostapd_lang_string *s = &p->friendly_name[i]; + wpabuf_put_u8(buf, 3 + s->name_len); + wpabuf_put_data(buf, s->lang, 3); + wpabuf_put_data(buf, s->name, s->name_len); + } + WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); + + /* OSU Server URI */ + if (p->server_uri) { + wpabuf_put_u8(buf, os_strlen(p->server_uri)); + wpabuf_put_str(buf, p->server_uri); + } else + wpabuf_put_u8(buf, 0); + + /* OSU Method List */ + count = wpabuf_put(buf, 1); + for (i = 0; p->method_list[i] >= 0; i++) + wpabuf_put_u8(buf, p->method_list[i]); + *count = i; + + /* Icons Available */ + len2 = wpabuf_put(buf, 2); + for (i = 0; i < p->icons_count; i++) { + size_t j; + struct hs20_icon *icon = NULL; + + for (j = 0; j < bss->hs20_icons_count && !icon; j++) { + if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == + 0) + icon = &bss->hs20_icons[j]; + } + if (!icon) + continue; /* icon info not found */ + + wpabuf_put_le16(buf, icon->width); + wpabuf_put_le16(buf, icon->height); + wpabuf_put_data(buf, icon->language, 3); + wpabuf_put_u8(buf, os_strlen(icon->type)); + wpabuf_put_str(buf, icon->type); + wpabuf_put_u8(buf, os_strlen(icon->name)); + wpabuf_put_str(buf, icon->name); + } + WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); + + /* OSU_NAI */ + if (p->osu_nai) { + wpabuf_put_u8(buf, os_strlen(p->osu_nai)); + wpabuf_put_str(buf, p->osu_nai); + } else + wpabuf_put_u8(buf, 0); + + /* OSU Service Description Duples */ + len2 = wpabuf_put(buf, 2); + for (i = 0; i < p->service_desc_count; i++) { + struct hostapd_lang_string *s = &p->service_desc[i]; + wpabuf_put_u8(buf, 3 + s->name_len); + wpabuf_put_data(buf, s->lang, 3); + wpabuf_put_data(buf, s->name, s->name_len); + } + WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); + + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +static void anqp_add_osu_providers_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_osu_providers_count) { + size_t i; + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + + /* OSU SSID */ + wpabuf_put_u8(buf, hapd->conf->osu_ssid_len); + wpabuf_put_data(buf, hapd->conf->osu_ssid, + hapd->conf->osu_ssid_len); + + /* Number of OSU Providers */ + wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count); + + for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { + anqp_add_osu_provider( + buf, hapd->conf, + &hapd->conf->hs20_osu_providers[i]); + } + + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_icon_binary_file(struct hostapd_data *hapd, + struct wpabuf *buf, + const u8 *name, size_t name_len) +{ + struct hs20_icon *icon; + size_t i; + u8 *len; + + wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename", + name, name_len); + for (i = 0; i < hapd->conf->hs20_icons_count; i++) { + icon = &hapd->conf->hs20_icons[i]; + if (name_len == os_strlen(icon->name) && + os_memcmp(name, icon->name, name_len) == 0) + break; + } + + if (i < hapd->conf->hs20_icons_count) + icon = &hapd->conf->hs20_icons[i]; + else + icon = NULL; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE); + wpabuf_put_u8(buf, 0); /* Reserved */ + + if (icon) { + char *data; + size_t data_len; + + data = os_readfile(icon->file, &data_len); + if (data == NULL || data_len > 65535) { + wpabuf_put_u8(buf, 2); /* Download Status: + * Unspecified file error */ + wpabuf_put_u8(buf, 0); + wpabuf_put_le16(buf, 0); + } else { + wpabuf_put_u8(buf, 0); /* Download Status: Success */ + wpabuf_put_u8(buf, os_strlen(icon->type)); + wpabuf_put_str(buf, icon->type); + wpabuf_put_le16(buf, data_len); + wpabuf_put_data(buf, data, data_len); + } + os_free(data); + } else { + wpabuf_put_u8(buf, 1); /* Download Status: File not found */ + wpabuf_put_u8(buf, 0); + wpabuf_put_le16(buf, 0); + } + + gas_anqp_set_element_len(buf, len); +} + #endif /* CONFIG_HS20 */ static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, - struct gas_dialog_info *di, - const u8 *home_realm, size_t home_realm_len) + const u8 *home_realm, size_t home_realm_len, + const u8 *icon_name, size_t icon_name_len) { struct wpabuf *buf; + size_t len; + + len = 1400; + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) + len += 1000; + if (request & ANQP_REQ_ICON_REQUEST) + len += 65536; - buf = wpabuf_alloc(1400); + buf = wpabuf_alloc(len); if (buf == NULL) return NULL; @@ -550,44 +732,32 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, anqp_add_connection_capability(hapd, buf); if (request & ANQP_REQ_OPERATING_CLASS) anqp_add_operating_class(hapd, buf); + if (request & ANQP_REQ_OSU_PROVIDERS_LIST) + anqp_add_osu_providers_list(hapd, buf); + if (request & ANQP_REQ_ICON_REQUEST) + anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); #endif /* CONFIG_HS20 */ return buf; } -static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) -{ - struct gas_dialog_info *dia = eloop_data; - - wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for " - "dialog token %d", dia->dialog_token); - - gas_serv_dialog_clear(dia); -} - - struct anqp_query_info { unsigned int request; - unsigned int remote_request; const u8 *home_realm_query; size_t home_realm_query_len; - u16 remote_delay; + const u8 *icon_name; + size_t icon_name_len; + int p2p_sd; }; static void set_anqp_req(unsigned int bit, const char *name, int local, - unsigned int remote, u16 remote_delay, struct anqp_query_info *qi) { qi->request |= bit; if (local) { wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); - } else if (bit & remote) { - wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name); - qi->remote_request |= bit; - if (remote_delay > qi->remote_delay) - qi->remote_delay = remote_delay; } else { wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); } @@ -599,43 +769,38 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, { switch (info_id) { case ANQP_CAPABILITY_LIST: - set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0, - 0, qi); + set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, + qi); break; case ANQP_VENUE_NAME: set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", - hapd->conf->venue_name != NULL, 0, 0, qi); + hapd->conf->venue_name != NULL, qi); break; case ANQP_NETWORK_AUTH_TYPE: set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", - hapd->conf->network_auth_type != NULL, - 0, 0, qi); + hapd->conf->network_auth_type != NULL, qi); break; case ANQP_ROAMING_CONSORTIUM: set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", - hapd->conf->roaming_consortium != NULL, 0, 0, qi); + hapd->conf->roaming_consortium != NULL, qi); break; case ANQP_IP_ADDR_TYPE_AVAILABILITY: set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, "IP Addr Type Availability", - hapd->conf->ipaddr_type_configured, - 0, 0, qi); + hapd->conf->ipaddr_type_configured, qi); break; case ANQP_NAI_REALM: set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", - hapd->conf->nai_realm_data != NULL, - 0, 0, qi); + hapd->conf->nai_realm_data != NULL, qi); break; case ANQP_3GPP_CELLULAR_NETWORK: set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, "3GPP Cellular Network", - hapd->conf->anqp_3gpp_cell_net != NULL, - 0, 0, qi); + hapd->conf->anqp_3gpp_cell_net != NULL, qi); break; case ANQP_DOMAIN_NAME: set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", - hapd->conf->domain_name != NULL, - 0, 0, qi); + hapd->conf->domain_name != NULL, qi); break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", @@ -667,29 +832,30 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, switch (subtype) { case HS20_STYPE_CAPABILITY_LIST: set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", - 1, 0, 0, qi); + 1, qi); break; case HS20_STYPE_OPERATOR_FRIENDLY_NAME: set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, "Operator Friendly Name", - hapd->conf->hs20_oper_friendly_name != NULL, - 0, 0, qi); + hapd->conf->hs20_oper_friendly_name != NULL, qi); break; case HS20_STYPE_WAN_METRICS: set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", - hapd->conf->hs20_wan_metrics != NULL, - 0, 0, qi); + hapd->conf->hs20_wan_metrics != NULL, qi); break; case HS20_STYPE_CONNECTION_CAPABILITY: set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, "Connection Capability", hapd->conf->hs20_connection_capability != NULL, - 0, 0, qi); + qi); break; case HS20_STYPE_OPERATING_CLASS: set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", - hapd->conf->hs20_operating_class != NULL, - 0, 0, qi); + hapd->conf->hs20_operating_class != NULL, qi); + break; + case HS20_STYPE_OSU_PROVIDERS_LIST: + set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", + hapd->conf->hs20_osu_providers_count, qi); break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", @@ -716,6 +882,23 @@ static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, } +static void rx_anqp_hs_icon_request(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + qi->request |= ANQP_REQ_ICON_REQUEST; + qi->icon_name = pos; + qi->icon_name_len = end - pos; + if (hapd->conf->hs20_icons_count) { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query " + "(local)"); + } else { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not " + "available"); + } +} + + static void rx_anqp_vendor_specific(struct hostapd_data *hapd, const u8 *pos, const u8 *end, struct anqp_query_info *qi) @@ -737,6 +920,21 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, return; } +#ifdef CONFIG_P2P + if (*pos == P2P_OUI_TYPE) { + /* + * This is for P2P SD and will be taken care of by the P2P + * implementation. This query needs to be ignored in the generic + * GAS server to avoid duplicated response. + */ + wpa_printf(MSG_DEBUG, + "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", + *pos); + qi->p2p_sd = 1; + return; + } +#endif /* CONFIG_P2P */ + if (*pos != HS20_ANQP_OUI_TYPE) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", *pos); @@ -760,6 +958,9 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, case HS20_STYPE_NAI_HOME_REALM_QUERY: rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); break; + case HS20_STYPE_ICON_REQUEST: + rx_anqp_hs_icon_request(hapd, pos, end, qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " "%u", subtype); @@ -772,17 +973,26 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, - struct anqp_query_info *qi) + struct anqp_query_info *qi, int prot) { struct wpabuf *buf, *tx_buf; - buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, + buf = gas_serv_build_gas_resp_payload(hapd, qi->request, qi->home_realm_query, - qi->home_realm_query_len); + qi->home_realm_query_len, + qi->icon_name, qi->icon_name_len); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) return; +#ifdef CONFIG_P2P + if (wpabuf_len(buf) == 0 && qi->p2p_sd) { + wpa_printf(MSG_DEBUG, + "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)"); + wpabuf_free(buf); + return; + } +#endif /* CONFIG_P2P */ if (wpabuf_len(buf) > hapd->gas_frag_limit || hapd->conf->gas_comeback_delay) { @@ -802,13 +1012,17 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, "for " MACSTR " (dialog token %u)", MAC2STR(sa), dialog_token); wpabuf_free(buf); - return; + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, NULL); + } else { + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, NULL); } - di->sd_resp = buf; - di->sd_resp_pos = 0; - tx_buf = gas_anqp_build_initial_resp_buf( - dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, - NULL); } else { wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); tx_buf = gas_anqp_build_initial_resp_buf( @@ -817,7 +1031,8 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, } if (!tx_buf) return; - + if (prot) + convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); @@ -826,7 +1041,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len) + const u8 *data, size_t len, int prot) { const u8 *pos = data; const u8 *end = data + len; @@ -876,6 +1091,8 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, return; wpabuf_put_data(buf, adv_proto, 2 + slen); wpabuf_put_le16(buf, 0); /* Query Response Length */ + if (prot) + convert_to_protected_dual(buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); @@ -927,100 +1144,13 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, pos += elen; } - gas_serv_req_local_processing(hapd, sa, dialog_token, &qi); -} - - -void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, - struct gas_dialog_info *dialog) -{ - struct wpabuf *buf, *tx_buf; - u8 dialog_token = dialog->dialog_token; - size_t frag_len; - - if (dialog->sd_resp == NULL) { - buf = gas_serv_build_gas_resp_payload(hapd, - dialog->all_requested, - dialog, NULL, 0); - wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", - buf); - if (!buf) - goto tx_gas_response_done; - dialog->sd_resp = buf; - dialog->sd_resp_pos = 0; - } - frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; - if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || - hapd->conf->gas_comeback_delay) { - u16 comeback_delay_tus = dialog->comeback_delay + - GAS_SERV_COMEBACK_DELAY_FUDGE; - u32 comeback_delay_secs, comeback_delay_usecs; - - if (hapd->conf->gas_comeback_delay) { - /* Testing - allow overriding of the delay value */ - comeback_delay_tus = hapd->conf->gas_comeback_delay; - } - - wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " - "%u) and comeback delay %u, " - "requesting comebacks", (unsigned int) frag_len, - (unsigned int) hapd->gas_frag_limit, - dialog->comeback_delay); - tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, - WLAN_STATUS_SUCCESS, - comeback_delay_tus, - NULL); - if (tx_buf) { - wpa_msg(hapd->msg_ctx, MSG_DEBUG, - "GAS: Tx GAS Initial Resp (comeback = 10TU)"); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, - dst, - wpabuf_head(tx_buf), - wpabuf_len(tx_buf)); - } - wpabuf_free(tx_buf); - - /* start a timer of 1.5 * comeback-delay */ - comeback_delay_tus = comeback_delay_tus + - (comeback_delay_tus / 2); - comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; - comeback_delay_usecs = (comeback_delay_tus * 1024) - - (comeback_delay_secs * 1000000); - eloop_register_timeout(comeback_delay_secs, - comeback_delay_usecs, - gas_serv_clear_cached_ies, dialog, - NULL); - goto tx_gas_response_done; - } - - buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + - dialog->sd_resp_pos, frag_len); - if (buf == NULL) { - wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " - "failed"); - goto tx_gas_response_done; - } - tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, - WLAN_STATUS_SUCCESS, 0, buf); - wpabuf_free(buf); - if (tx_buf == NULL) - goto tx_gas_response_done; - wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " - "Response (frag_id %d frag_len %d)", - dialog->sd_frag_id, (int) frag_len); - dialog->sd_frag_id++; - - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); - wpabuf_free(tx_buf); -tx_gas_response_done: - gas_serv_clear_cached_ies(dialog, NULL); + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); } static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len) + const u8 *data, size_t len, int prot) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; @@ -1051,33 +1181,6 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, goto send_resp; } - if (dialog->sd_resp == NULL) { - wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", - dialog->requested, dialog->received); - if ((dialog->requested & dialog->received) != - dialog->requested) { - wpa_printf(MSG_DEBUG, "GAS: Did not receive response " - "from remote processing"); - gas_serv_dialog_clear(dialog); - tx_buf = gas_anqp_build_comeback_resp_buf( - dialog_token, - WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, - NULL); - if (tx_buf == NULL) - return; - goto send_resp; - } - - buf = gas_serv_build_gas_resp_payload(hapd, - dialog->all_requested, - dialog, NULL, 0); - wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", - buf); - if (!buf) - goto rx_gas_comeback_req_done; - dialog->sd_resp = buf; - dialog->sd_resp_pos = 0; - } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; if (frag_len > hapd->gas_frag_limit) { frag_len = hapd->gas_frag_limit; @@ -1090,15 +1193,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, if (buf == NULL) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " "buffer"); - goto rx_gas_comeback_req_done; + gas_serv_dialog_clear(dialog); + return; } tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, more, 0, buf); wpabuf_free(buf); - if (tx_buf == NULL) - goto rx_gas_comeback_req_done; + if (tx_buf == NULL) { + gas_serv_dialog_clear(dialog); + return; + } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " "(frag_id %d more=%d frag_len=%d)", dialog->sd_frag_id, more, (int) frag_len); @@ -1118,13 +1224,11 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, } send_resp: + if (prot) + convert_to_protected_dual(tx_buf); hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, wpabuf_head(tx_buf), wpabuf_len(tx_buf)); wpabuf_free(tx_buf); - return; - -rx_gas_comeback_req_done: - gas_serv_clear_cached_ies(dialog, NULL); } @@ -1133,24 +1237,30 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, { struct hostapd_data *hapd = ctx; const struct ieee80211_mgmt *mgmt; - size_t hdr_len; const u8 *sa, *data; + int prot; mgmt = (const struct ieee80211_mgmt *) buf; - hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; - if (hdr_len > len) + if (len < IEEE80211_HDRLEN + 2) return; - if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL) return; + /* + * Note: Public Action and Protected Dual of Public Action frames share + * the same payload structure, so it is fine to use definitions of + * Public Action frames to process both. + */ + prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; sa = mgmt->sa; - len -= hdr_len; - data = &mgmt->u.action.u.public_action.action; + len -= IEEE80211_HDRLEN + 1; + data = buf + IEEE80211_HDRLEN + 1; switch (data[0]) { case WLAN_PA_GAS_INITIAL_REQ: - gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1); + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); break; case WLAN_PA_GAS_COMEBACK_REQ: - gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1); + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); break; } } @@ -1158,8 +1268,8 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, int gas_serv_init(struct hostapd_data *hapd) { - hapd->public_action_cb = gas_serv_rx_public_action; - hapd->public_action_cb_ctx = hapd; + hapd->public_action_cb2 = gas_serv_rx_public_action; + hapd->public_action_cb2_ctx = hapd; hapd->gas_frag_limit = 1400; if (hapd->conf->gas_frag_limit > 0) hapd->gas_frag_limit = hapd->conf->gas_frag_limit; diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 4213cf6da020..4ec3201967c0 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -1,6 +1,6 @@ /* * Generic advertisement service (GAS) server - * Copyright (c) 2011-2012, Qualcomm Atheros, 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. @@ -37,29 +37,22 @@ (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) #define ANQP_REQ_OPERATING_CLASS \ (0x10000 << HS20_STYPE_OPERATING_CLASS) - -/* To account for latencies between hostapd and external ANQP processor */ -#define GAS_SERV_COMEBACK_DELAY_FUDGE 10 -#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */ +#define ANQP_REQ_OSU_PROVIDERS_LIST \ + (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST) +#define ANQP_REQ_ICON_REQUEST \ + (0x10000 << HS20_STYPE_ICON_REQUEST) struct gas_dialog_info { u8 valid; - u8 index; struct wpabuf *sd_resp; /* Fragmented response */ u8 dialog_token; size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; - u16 comeback_delay; - - unsigned int requested; - unsigned int received; - unsigned int all_requested; + int prot; /* whether Protected Dual of Public Action frame is used */ }; struct hostapd_data; -void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, - struct gas_dialog_info *dialog); struct gas_dialog_info * gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index cef9dafc52e0..3e4e16b4f396 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,9 +11,12 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "radius/radius_client.h" #include "radius/radius_das.h" -#include "drivers/driver.h" +#include "eap_server/tncs.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" #include "hostapd.h" #include "authsrv.h" #include "sta_info.h" @@ -32,14 +35,19 @@ #include "ap_config.h" #include "p2p_hostapd.h" #include "gas_serv.h" +#include "dfs.h" +#include "ieee802_11.h" +#include "bss_load.h" +#include "x_snoop.h" +#include "dhcp_snoop.h" +#include "ndisc_snoop.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); - -extern int wpa_debug_level; -extern struct wpa_driver_ops *wpa_drivers[]; +static int setup_interface2(struct hostapd_iface *iface); +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); int hostapd_for_each_interface(struct hapd_interfaces *interfaces, @@ -61,10 +69,21 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, static void hostapd_reload_bss(struct hostapd_data *hapd) { + struct hostapd_ssid *ssid; + #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ + ssid = &hapd->conf->ssid; + if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next && + ssid->wpa_passphrase_set && ssid->wpa_passphrase) { + /* + * Force PSK to be derived again since SSID or passphrase may + * have changed. + */ + hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk); + } if (hostapd_setup_wpa_psk(hapd->conf)) { wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " "after reloading configuration"); @@ -75,7 +94,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd) else hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); - if (hapd->conf->wpa && hapd->wpa_auth == NULL) { + if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) { hostapd_setup_wpa(hapd); if (hapd->wpa_auth) wpa_init_keys(hapd->wpa_auth); @@ -108,19 +127,10 @@ static void hostapd_reload_bss(struct hostapd_data *hapd) } -int hostapd_reload_config(struct hostapd_iface *iface) +static void hostapd_clear_old(struct hostapd_iface *iface) { - struct hostapd_data *hapd = iface->bss[0]; - struct hostapd_config *newconf, *oldconf; size_t j; - if (iface->interfaces == NULL || - iface->interfaces->config_read_cb == NULL) - return -1; - newconf = iface->interfaces->config_read_cb(iface->config_fname); - if (newconf == NULL) - return -1; - /* * Deauthenticate all stations since the new configuration may not * allow them to use the BSS anymore. @@ -136,6 +146,31 @@ int hostapd_reload_config(struct hostapd_iface *iface) radius_client_flush(iface->bss[j]->radius, 0); #endif /* CONFIG_NO_RADIUS */ } +} + + +int hostapd_reload_config(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + size_t j; + + if (iface->config_fname == NULL) { + /* Only in-memory config in use - assume it has been updated */ + hostapd_clear_old(iface); + for (j = 0; j < iface->num_bss; j++) + hostapd_reload_bss(iface->bss[j]); + return 0; + } + + if (iface->interfaces == NULL || + iface->interfaces->config_read_cb == NULL) + return -1; + newconf = iface->interfaces->config_read_cb(iface->config_fname); + if (newconf == NULL) + return -1; + + hostapd_clear_old(iface); oldconf = hapd->iconf; iface->conf = newconf; @@ -143,7 +178,18 @@ int hostapd_reload_config(struct hostapd_iface *iface) for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; hapd->iconf = newconf; - hapd->conf = &newconf->bss[j]; + hapd->iconf->channel = oldconf->channel; + hapd->iconf->secondary_channel = oldconf->secondary_channel; + hapd->iconf->ieee80211n = oldconf->ieee80211n; + hapd->iconf->ieee80211ac = oldconf->ieee80211ac; + hapd->iconf->ht_capab = oldconf->ht_capab; + hapd->iconf->vht_capab = oldconf->vht_capab; + hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth; + hapd->iconf->vht_oper_centr_freq_seg0_idx = + oldconf->vht_oper_centr_freq_seg0_idx; + hapd->iconf->vht_oper_centr_freq_seg1_idx = + oldconf->vht_oper_centr_freq_seg1_idx; + hapd->conf = newconf->bss[j]; hostapd_reload_bss(hapd); } @@ -205,36 +251,30 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) errors++; } - if (ssid->dyn_vlan_keys) { - size_t i; - for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { - const char *ifname; - struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i]; - if (key == NULL) - continue; - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, - i); - if (ifname == NULL) - continue; - - idx = key->idx; - if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, - broadcast_ether_addr, idx, 1, - NULL, 0, key->key[idx], - key->len[idx])) { - wpa_printf(MSG_WARNING, "Could not set " - "dynamic VLAN WEP encryption."); - errors++; - } - } - } - return errors; } static void hostapd_free_hapd_data(struct hostapd_data *hapd) { + os_free(hapd->probereq_cb); + hapd->probereq_cb = NULL; + +#ifdef CONFIG_P2P + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = NULL; + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = NULL; +#endif /* CONFIG_P2P */ + + if (!hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", + __func__, hapd->conf->iface); + return; + } + hapd->started = 0; + + wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); iapp_deinit(hapd->iapp); hapd->iapp = NULL; accounting_deinit(hapd); @@ -252,32 +292,45 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) authsrv_deinit(hapd); - if (hapd->interface_added && - hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) { - wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", - hapd->conf->iface); + if (hapd->interface_added) { + hapd->interface_added = 0; + if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) { + wpa_printf(MSG_WARNING, + "Failed to remove BSS interface %s", + hapd->conf->iface); + hapd->interface_added = 1; + } else { + /* + * Since this was a dynamically added interface, the + * driver wrapper may have removed its internal instance + * and hapd->drv_priv is not valid anymore. + */ + hapd->drv_priv = NULL; + } } - os_free(hapd->probereq_cb); - hapd->probereq_cb = NULL; - -#ifdef CONFIG_P2P - wpabuf_free(hapd->p2p_beacon_ie); - hapd->p2p_beacon_ie = NULL; - wpabuf_free(hapd->p2p_probe_resp_ie); - hapd->p2p_probe_resp_ie = NULL; -#endif /* CONFIG_P2P */ - wpabuf_free(hapd->time_adv); #ifdef CONFIG_INTERWORKING gas_serv_deinit(hapd); #endif /* CONFIG_INTERWORKING */ + bss_load_update_deinit(hapd); + ndisc_snoop_deinit(hapd); + dhcp_snoop_deinit(hapd); + x_snoop_deinit(hapd); + #ifdef CONFIG_SQLITE - os_free(hapd->tmp_eap_user.identity); - os_free(hapd->tmp_eap_user.password); + bin_clear_free(hapd->tmp_eap_user.identity, + hapd->tmp_eap_user.identity_len); + bin_clear_free(hapd->tmp_eap_user.password, + hapd->tmp_eap_user.password_len); #endif /* CONFIG_SQLITE */ + +#ifdef CONFIG_MESH + wpabuf_free(hapd->mesh_pending_auth); + hapd->mesh_pending_auth = NULL; +#endif /* CONFIG_MESH */ } @@ -286,13 +339,13 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) * @hapd: Pointer to BSS data * * This function is used to free all per-BSS data structures and resources. - * This gets called in a loop for each BSS between calls to - * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface - * is deinitialized. Most of the modules that are initialized in - * hostapd_setup_bss() are deinitialized here. + * Most of the modules that are initialized in hostapd_setup_bss() are + * deinitialized here. */ static void hostapd_cleanup(struct hostapd_data *hapd) { + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, + hapd->conf->iface); if (hapd->iface->interfaces && hapd->iface->interfaces->ctrl_iface_deinit) hapd->iface->interfaces->ctrl_iface_deinit(hapd); @@ -300,20 +353,14 @@ static void hostapd_cleanup(struct hostapd_data *hapd) } -/** - * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup - * @iface: Pointer to interface data - * - * This function is called before per-BSS data structures are deinitialized - * with hostapd_cleanup(). - */ -static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) -{ -} - - static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) { + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + hostapd_stop_setup_timers(iface); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; os_free(iface->current_rates); @@ -333,19 +380,23 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) */ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); iface->conf = NULL; os_free(iface->config_fname); os_free(iface->bss); + wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface); os_free(iface); } static void hostapd_clear_wep(struct hostapd_data *hapd) { - if (hapd->drv_priv) { + if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) { hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); } @@ -396,11 +447,15 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) return 0; - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries"); - if (hostapd_flush(hapd)) { - wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to " - "kernel driver"); - ret = -1; + if (!hapd->iface->driver_ap_teardown) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Flushing old station entries"); + + if (hostapd_flush(hapd)) { + wpa_msg(hapd->msg_ctx, MSG_WARNING, + "Could not connect to kernel driver"); + ret = -1; + } } wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); os_memset(addr, 0xff, ETH_ALEN); @@ -411,6 +466,14 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) } +static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd) +{ + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); +} + + /** * hostapd_validate_bssid_configuration - Validate BSSID configuration * @iface: Pointer to interface data @@ -437,7 +500,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) /* Determine the bits necessary to any configured BSSIDs, if they are higher than the number of BSSIDs. */ for (j = 0; j < iface->conf->num_bss; j++) { - if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) { + if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { if (j) auto_addr++; continue; @@ -445,7 +508,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) for (i = 0; i < ETH_ALEN; i++) { mask[i] |= - iface->conf->bss[j].bssid[i] ^ + iface->conf->bss[j]->bssid[i] ^ hapd->own_addr[i]; } } @@ -510,7 +573,7 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a) size_t i; for (i = 0; i < conf->num_bss; i++) { - if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) { + if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) { return 1; } } @@ -524,57 +587,223 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a) static int hostapd_das_nas_mismatch(struct hostapd_data *hapd, struct radius_das_attrs *attr) { - /* TODO */ + if (attr->nas_identifier && + (!hapd->conf->nas_identifier || + os_strlen(hapd->conf->nas_identifier) != + attr->nas_identifier_len || + os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier, + attr->nas_identifier_len) != 0)) { + wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch"); + return 1; + } + + if (attr->nas_ip_addr && + (hapd->conf->own_ip_addr.af != AF_INET || + os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) != + 0)) { + wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch"); + return 1; + } + +#ifdef CONFIG_IPV6 + if (attr->nas_ipv6_addr && + (hapd->conf->own_ip_addr.af != AF_INET6 || + os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16) + != 0)) { + wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch"); + return 1; + } +#endif /* CONFIG_IPV6 */ + return 0; } static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, - struct radius_das_attrs *attr) + struct radius_das_attrs *attr, + int *multi) { - struct sta_info *sta = NULL; + struct sta_info *selected, *sta; char buf[128]; + int num_attr = 0; + int count; + + *multi = 0; - if (attr->sta_addr) + for (sta = hapd->sta_list; sta; sta = sta->next) + sta->radius_das_match = 1; + + if (attr->sta_addr) { + num_attr++; sta = ap_get_sta(hapd, attr->sta_addr); + if (!sta) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: No Calling-Station-Id match"); + return NULL; + } + + selected = sta; + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta != selected) + sta->radius_das_match = 0; + } + wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match"); + } + + if (attr->acct_session_id) { + num_attr++; + if (attr->acct_session_id_len != 17) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Acct-Session-Id cannot match"); + return NULL; + } + count = 0; - if (sta == NULL && attr->acct_session_id && - attr->acct_session_id_len == 17) { for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!sta->radius_das_match) + continue; os_snprintf(buf, sizeof(buf), "%08X-%08X", sta->acct_session_id_hi, sta->acct_session_id_lo); - if (os_memcmp(attr->acct_session_id, buf, 17) == 0) - break; + if (os_memcmp(attr->acct_session_id, buf, 17) != 0) + sta->radius_das_match = 0; + else + count++; + } + + if (count == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: No matches remaining after Acct-Session-Id check"); + return NULL; + } + wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match"); + } + + if (attr->acct_multi_session_id) { + num_attr++; + if (attr->acct_multi_session_id_len != 17) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Acct-Multi-Session-Id cannot match"); + return NULL; + } + count = 0; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!sta->radius_das_match) + continue; + if (!sta->eapol_sm || + !sta->eapol_sm->acct_multi_session_id_hi) { + sta->radius_das_match = 0; + continue; + } + os_snprintf(buf, sizeof(buf), "%08X+%08X", + sta->eapol_sm->acct_multi_session_id_hi, + sta->eapol_sm->acct_multi_session_id_lo); + if (os_memcmp(attr->acct_multi_session_id, buf, 17) != + 0) + sta->radius_das_match = 0; + else + count++; + } + + if (count == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check"); + return NULL; } + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Acct-Multi-Session-Id match"); } - if (sta == NULL && attr->cui) { + if (attr->cui) { + num_attr++; + count = 0; + for (sta = hapd->sta_list; sta; sta = sta->next) { struct wpabuf *cui; + + if (!sta->radius_das_match) + continue; cui = ieee802_1x_get_radius_cui(sta->eapol_sm); - if (cui && wpabuf_len(cui) == attr->cui_len && + if (!cui || wpabuf_len(cui) != attr->cui_len || os_memcmp(wpabuf_head(cui), attr->cui, - attr->cui_len) == 0) - break; + attr->cui_len) != 0) + sta->radius_das_match = 0; + else + count++; } + + if (count == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: No matches remaining after Chargeable-User-Identity check"); + return NULL; + } + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Chargeable-User-Identity match"); } - if (sta == NULL && attr->user_name) { + if (attr->user_name) { + num_attr++; + count = 0; + for (sta = hapd->sta_list; sta; sta = sta->next) { u8 *identity; size_t identity_len; + + if (!sta->radius_das_match) + continue; identity = ieee802_1x_get_identity(sta->eapol_sm, &identity_len); - if (identity && - identity_len == attr->user_name_len && + if (!identity || + identity_len != attr->user_name_len || os_memcmp(identity, attr->user_name, identity_len) - == 0) - break; + != 0) + sta->radius_das_match = 0; + else + count++; + } + + if (count == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: No matches remaining after User-Name check"); + return NULL; + } + wpa_printf(MSG_DEBUG, + "RADIUS DAS: User-Name match"); + } + + if (num_attr == 0) { + /* + * In theory, we could match all current associations, but it + * seems safer to just reject requests that do not include any + * session identification attributes. + */ + wpa_printf(MSG_DEBUG, + "RADIUS DAS: No session identification attributes included"); + return NULL; + } + + selected = NULL; + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->radius_das_match) { + if (selected) { + *multi = 1; + return NULL; + } + selected = sta; } } - return sta; + return selected; +} + + +static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + if (!hapd->wpa_auth) + return -1; + return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr); } @@ -583,13 +812,30 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) { struct hostapd_data *hapd = ctx; struct sta_info *sta; + int multi; if (hostapd_das_nas_mismatch(hapd, attr)) return RADIUS_DAS_NAS_MISMATCH; - sta = hostapd_das_find_sta(hapd, attr); - if (sta == NULL) + sta = hostapd_das_find_sta(hapd, attr, &multi); + if (sta == NULL) { + if (multi) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Multiple sessions match - not supported"); + return RADIUS_DAS_MULTI_SESSION_MATCH; + } + if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: PMKSA cache entry matched"); + return RADIUS_DAS_SUCCESS; + } + wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found"); return RADIUS_DAS_SESSION_NOT_FOUND; + } + + wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR + " - disconnecting", MAC2STR(sta->addr)); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); hostapd_drv_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -604,7 +850,8 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) /** * hostapd_setup_bss - Per-BSS setup (initialization) * @hapd: Pointer to BSS data - * @first: Whether this BSS is the first BSS of an interface + * @first: Whether this BSS is the first BSS of an interface; -1 = not first, + * but interface may exist * * This function is used to initialize all per-BSS data structures and * resources. This gets called in a loop for each BSS when an interface is @@ -618,35 +865,54 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) int ssid_len, set_ssid; char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; + int flush_old_stations = 1; + + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", + __func__, hapd, conf->iface, first); + +#ifdef EAP_SERVER_TNC + if (conf->tnc && tncs_global_init() < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); + return -1; + } +#endif /* EAP_SERVER_TNC */ - if (!first) { - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { + if (hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s was already started", + __func__, conf->iface); + return -1; + } + hapd->started = 1; + + if (!first || first == -1) { + if (hostapd_mac_comp_empty(conf->bssid) == 0) { /* Allocate the next available BSSID. */ do { inc_byte_array(hapd->own_addr, ETH_ALEN); } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } else { /* Allocate the configured BSSID. */ - os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); + os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); if (hostapd_mac_comp(hapd->own_addr, hapd->iface->bss[0]->own_addr) == 0) { wpa_printf(MSG_ERROR, "BSS '%s' may not have " "BSSID set to the MAC address of " - "the radio", hapd->conf->iface); + "the radio", conf->iface); return -1; } } hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, - hapd->conf->iface, hapd->own_addr, hapd, + conf->iface, hapd->own_addr, hapd, &hapd->drv_priv, force_ifname, if_addr, - hapd->conf->bridge[0] ? hapd->conf->bridge : - NULL)) { + conf->bridge[0] ? conf->bridge : NULL, + first == -1)) { wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" MACSTR ")", MAC2STR(hapd->own_addr)); + hapd->interface_added = 0; return -1; } } @@ -654,11 +920,18 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; - hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); +#ifdef CONFIG_MESH + if (hapd->iface->mconf == NULL) + flush_old_stations = 0; +#endif /* CONFIG_MESH */ + + if (flush_old_stations) + hostapd_flush_old_stations(hapd, + WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); - if (hostapd_setup_encryption(hapd->conf->iface, hapd)) + if (hostapd_setup_encryption(conf->iface, hapd)) return -1; /* @@ -692,9 +965,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (!hostapd_drv_none(hapd)) { wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR " and ssid \"%s\"", - hapd->conf->iface, MAC2STR(hapd->own_addr), - wpa_ssid_txt(hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len)); + conf->iface, MAC2STR(hapd->own_addr), + wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len)); } if (hostapd_setup_wpa_psk(conf)) { @@ -710,7 +982,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (wpa_debug_level == MSG_MSGDUMP) + if (wpa_debug_level <= MSG_MSGDUMP) conf->radius->msg_dumps = 1; #ifndef CONFIG_NO_RADIUS hapd->radius = radius_client_init(hapd, conf->radius); @@ -719,17 +991,17 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (hapd->conf->radius_das_port) { + if (conf->radius_das_port) { struct radius_das_conf das_conf; os_memset(&das_conf, 0, sizeof(das_conf)); - das_conf.port = hapd->conf->radius_das_port; - das_conf.shared_secret = hapd->conf->radius_das_shared_secret; + das_conf.port = conf->radius_das_port; + das_conf.shared_secret = conf->radius_das_shared_secret; das_conf.shared_secret_len = - hapd->conf->radius_das_shared_secret_len; - das_conf.client_addr = &hapd->conf->radius_das_client_addr; - das_conf.time_window = hapd->conf->radius_das_time_window; + conf->radius_das_shared_secret_len; + das_conf.client_addr = &conf->radius_das_client_addr; + das_conf.time_window = conf->radius_das_time_window; das_conf.require_event_timestamp = - hapd->conf->radius_das_require_event_timestamp; + conf->radius_das_require_event_timestamp; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; hapd->radius_das = radius_das_init(&das_conf); @@ -756,7 +1028,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (hapd->conf->wpa && hostapd_setup_wpa(hapd)) + if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd)) return -1; if (accounting_init(hapd)) { @@ -764,8 +1036,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) return -1; } - if (hapd->conf->ieee802_11f && - (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { + if (conf->ieee802_11f && + (hapd->iapp = iapp_init(hapd, conf->iapp_iface)) == NULL) { wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " "failed."); return -1; @@ -776,21 +1048,47 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) wpa_printf(MSG_ERROR, "GAS server initialization failed"); return -1; } + + if (conf->qos_map_set_len && + hostapd_drv_set_qos_map(hapd, conf->qos_map_set, + conf->qos_map_set_len)) { + wpa_printf(MSG_ERROR, "Failed to initialize QoS Map"); + return -1; + } #endif /* CONFIG_INTERWORKING */ - if (hapd->iface->interfaces && - hapd->iface->interfaces->ctrl_iface_init && - hapd->iface->interfaces->ctrl_iface_init(hapd)) { - wpa_printf(MSG_ERROR, "Failed to setup control interface"); + if (conf->bss_load_update_period && bss_load_update_init(hapd)) { + wpa_printf(MSG_ERROR, "BSS Load initialization failed"); return -1; } + if (conf->proxy_arp) { + if (x_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "Generic snooping infrastructure initialization failed"); + return -1; + } + + if (dhcp_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "DHCP snooping initialization failed"); + return -1; + } + + if (ndisc_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "Neighbor Discovery snooping initialization failed"); + return -1; + } + } + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { wpa_printf(MSG_ERROR, "VLAN initialization failed."); return -1; } - ieee802_11_set_beacon(hapd); + if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0) + return -1; if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) return -1; @@ -808,6 +1106,11 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) int i; struct hostapd_tx_queue_params *p; +#ifdef CONFIG_MESH + if (iface->mconf == NULL) + return; +#endif /* CONFIG_MESH */ + for (i = 0; i < NUM_TX_QUEUES; i++) { p = &iface->conf->tx_queue[i]; @@ -821,11 +1124,152 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) } +static int hostapd_set_acl_list(struct hostapd_data *hapd, + struct mac_acl_entry *mac_acl, + int n_entries, u8 accept_acl) +{ + struct hostapd_acl_params *acl_params; + int i, err; + + acl_params = os_zalloc(sizeof(*acl_params) + + (n_entries * sizeof(acl_params->mac_acl[0]))); + if (!acl_params) + return -ENOMEM; + + for (i = 0; i < n_entries; i++) + os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr, + ETH_ALEN); + + acl_params->acl_policy = accept_acl; + acl_params->num_mac_acl = n_entries; + + err = hostapd_drv_set_acl(hapd, acl_params); + + os_free(acl_params); + + return err; +} + + +static void hostapd_set_acl(struct hostapd_data *hapd) +{ + struct hostapd_config *conf = hapd->iconf; + int err; + u8 accept_acl; + + if (hapd->iface->drv_max_acl_mac_addrs == 0) + return; + + if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) { + accept_acl = 1; + err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac, + conf->bss[0]->num_accept_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set accept acl"); + return; + } + } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) { + accept_acl = 0; + err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac, + conf->bss[0]->num_deny_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set deny acl"); + return; + } + } +} + + +static int start_ctrl_iface_bss(struct hostapd_data *hapd) +{ + if (!hapd->iface->interfaces || + !hapd->iface->interfaces->ctrl_iface_init) + return 0; + + if (hapd->iface->interfaces->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, + "Failed to setup control interface for %s", + hapd->conf->iface); + return -1; + } + + return 0; +} + + +static int start_ctrl_iface(struct hostapd_iface *iface) +{ + size_t i; + + if (!iface->interfaces || !iface->interfaces->ctrl_iface_init) + return 0; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + if (iface->interfaces->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, + "Failed to setup control interface for %s", + hapd->conf->iface); + return -1; + } + } + + return 0; +} + + +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + + if (!iface->wait_channel_update) { + wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it"); + return; + } + + /* + * It is possible that the existing channel list is acceptable, so try + * to proceed. + */ + wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway"); + setup_interface2(iface); +} + + +void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator) +{ + if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER) + return; + + wpa_printf(MSG_DEBUG, "Channel list updated - continue setup"); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + setup_interface2(iface); +} + + static int setup_interface(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; size_t i; - char country[4]; + + /* + * It is possible that setup_interface() is called after the interface + * was disabled etc., in which case driver_ap_teardown is possibly set + * to 1. Clear it here so any other key/station deletion, which is not + * part of a teardown flow, would also call the relevant driver + * callbacks. + */ + iface->driver_ap_teardown = 0; + + if (!iface->phy[0]) { + const char *phy = hostapd_drv_get_radio_name(hapd); + if (phy) { + wpa_printf(MSG_DEBUG, "phy: %s", phy); + os_strlcpy(iface->phy, phy, sizeof(iface->phy)); + } + } /* * Make sure that all BSSes get configured with a pointer to the same @@ -839,15 +1283,49 @@ static int setup_interface(struct hostapd_iface *iface) if (hostapd_validate_bssid_configuration(iface)) return -1; + /* + * Initialize control interfaces early to allow external monitoring of + * channel setup operations that may take considerable amount of time + * especially for DFS cases. + */ + if (start_ctrl_iface(iface)) + return -1; + if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + char country[4], previous_country[4]; + + hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE); + if (hostapd_get_country(hapd, previous_country) < 0) + previous_country[0] = '\0'; + os_memcpy(country, hapd->iconf->country, 3); country[3] = '\0'; if (hostapd_set_country(hapd, country) < 0) { wpa_printf(MSG_ERROR, "Failed to set country code"); return -1; } + + wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s", + previous_country, country); + + if (os_strncmp(previous_country, country, 2) != 0) { + wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update"); + iface->wait_channel_update = 1; + eloop_register_timeout(5, 0, + channel_list_update_timeout, + iface, NULL); + return 0; + } } + return setup_interface2(iface); +} + + +static int setup_interface2(struct hostapd_iface *iface) +{ + iface->wait_channel_update = 0; + if (hostapd_get_hw_features(iface)) { /* Not all drivers support this yet, so continue without hw * feature data. */ @@ -856,48 +1334,117 @@ static int setup_interface(struct hostapd_iface *iface) if (ret < 0) { wpa_printf(MSG_ERROR, "Could not select hw_mode and " "channel. (%d)", ret); - return -1; + goto fail; + } + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)"); + return 0; } ret = hostapd_check_ht_capab(iface); if (ret < 0) - return -1; + goto fail; if (ret == 1) { wpa_printf(MSG_DEBUG, "Interface initialization will " "be completed in a callback"); return 0; } + + if (iface->conf->ieee80211h) + wpa_printf(MSG_DEBUG, "DFS support is enabled"); } return hostapd_setup_interface_complete(iface, 0); + +fail: + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + if (iface->interfaces && iface->interfaces->terminate_on_error) + eloop_terminate(); + return -1; } +/** + * hostapd_setup_interface_complete - Complete interface setup + * + * This function is called when previous steps in the interface setup has been + * completed. This can also start operations, e.g., DFS, that will require + * additional processing before interface is ready to be enabled. Such + * operations will call this function from eloop callbacks when finished. + */ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) { struct hostapd_data *hapd = iface->bss[0]; size_t j; u8 *prev_addr; + int delay_apply_cfg = 0; + int res_dfs_offload = 0; - if (err) { - wpa_printf(MSG_ERROR, "Interface initialization failed"); - eloop_terminate(); - return -1; - } + if (err) + goto fail; wpa_printf(MSG_DEBUG, "Completing interface initialization"); - if (hapd->iconf->channel) { - iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); + if (iface->conf->channel) { +#ifdef NEED_AP_MLME + int res; +#endif /* NEED_AP_MLME */ + + iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel); wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " "Frequency: %d MHz", - hostapd_hw_mode_txt(hapd->iconf->hw_mode), - hapd->iconf->channel, iface->freq); + hostapd_hw_mode_txt(iface->conf->hw_mode), + iface->conf->channel, iface->freq); + +#ifdef NEED_AP_MLME + /* Handle DFS only if it is not offloaded to the driver */ + if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) { + /* Check DFS */ + res = hostapd_handle_dfs(iface); + if (res <= 0) { + if (res < 0) + goto fail; + return res; + } + } else { + /* If DFS is offloaded to the driver */ + res_dfs_offload = hostapd_handle_dfs_offload(iface); + if (res_dfs_offload <= 0) { + if (res_dfs_offload < 0) + goto fail; + } else { + wpa_printf(MSG_DEBUG, + "Proceed with AP/channel setup"); + /* + * If this is a DFS channel, move to completing + * AP setup. + */ + if (res_dfs_offload == 1) + goto dfs_offload; + /* Otherwise fall through. */ + } + } +#endif /* NEED_AP_MLME */ + +#ifdef CONFIG_MESH + if (iface->mconf != NULL) { + wpa_printf(MSG_DEBUG, + "%s: Mesh configuration will be applied while joining the mesh network", + iface->bss[0]->conf->iface); + delay_apply_cfg = 1; + } +#endif /* CONFIG_MESH */ - if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, + if (!delay_apply_cfg && + hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, hapd->iconf->ieee80211n, - hapd->iconf->secondary_channel)) { + hapd->iconf->ieee80211ac, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + hapd->iconf->vht_oper_centr_freq_seg0_idx, + hapd->iconf->vht_oper_centr_freq_seg1_idx)) { wpa_printf(MSG_ERROR, "Could not set channel for " "kernel driver"); - return -1; + goto fail; } } @@ -908,7 +1455,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "Failed to prepare rates table."); - return -1; + goto fail; } } @@ -916,14 +1463,14 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " "kernel driver"); - return -1; + goto fail; } if (hapd->iconf->fragm_threshold > -1 && hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " "for kernel driver"); - return -1; + goto fail; } prev_addr = hapd->own_addr; @@ -932,20 +1479,29 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hapd = iface->bss[j]; if (j) os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); - if (hostapd_setup_bss(hapd, j == 0)) - return -1; + if (hostapd_setup_bss(hapd, j == 0)) { + do { + hapd = iface->bss[j]; + hostapd_bss_deinit_no_free(hapd); + hostapd_free_hapd_data(hapd); + } while (j-- > 0); + goto fail; + } if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) prev_addr = hapd->own_addr; } + hapd = iface->bss[0]; hostapd_tx_queue_params(iface); ap_list_init(iface); + hostapd_set_acl(hapd); + if (hostapd_driver_commit(hapd) < 0) { wpa_printf(MSG_ERROR, "%s: Failed to commit driver " "configuration", __func__); - return -1; + goto fail; } /* @@ -956,16 +1512,41 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) */ for (j = 0; j < iface->num_bss; j++) { if (hostapd_init_wps_complete(iface->bss[j])) - return -1; + goto fail; + } + + if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + !res_dfs_offload) { + /* + * If freq is DFS, and DFS is offloaded to the driver, then wait + * for CAC to complete. + */ + wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__); + return res_dfs_offload; } +#ifdef NEED_AP_MLME +dfs_offload: +#endif /* NEED_AP_MLME */ + hostapd_set_state(iface, HAPD_IFACE_ENABLED); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", iface->bss[0]->conf->iface); + if (iface->interfaces && iface->interfaces->terminate_on_error > 0) + iface->interfaces->terminate_on_error--; return 0; + +fail: + wpa_printf(MSG_ERROR, "Interface initialization failed"); + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + if (iface->interfaces && iface->interfaces->terminate_on_error) + eloop_terminate(); + return -1; } @@ -978,6 +1559,12 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) * and sets driver parameters based on the configuration. * Flushes old stations, sets the channel, encryption, * beacons, and WDS links based on the configuration. + * + * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS, + * or DFS operations, this function returns 0 before such operations have been + * completed. The pending operations are registered into eloop and will be + * completed from eloop callbacks. Those callbacks end up calling + * hostapd_setup_interface_complete() once setup has been completed. */ int hostapd_setup_interface(struct hostapd_iface *iface) { @@ -1027,68 +1614,327 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, } +static void hostapd_bss_deinit(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, + hapd->conf->iface); + hostapd_bss_deinit_no_free(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + hostapd_cleanup(hapd); +} + + void hostapd_interface_deinit(struct hostapd_iface *iface) { - size_t j; + int j; + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); if (iface == NULL) return; - hostapd_cleanup_iface_pre(iface); - for (j = 0; j < iface->num_bss; j++) { - struct hostapd_data *hapd = iface->bss[j]; - hostapd_free_stas(hapd); - hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); - hostapd_clear_wep(hapd); - hostapd_cleanup(hapd); - } + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + hostapd_stop_setup_timers(iface); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + iface->wait_channel_update = 0; + + for (j = iface->num_bss - 1; j >= 0; j--) + hostapd_bss_deinit(iface->bss[j]); } void hostapd_interface_free(struct hostapd_iface *iface) { size_t j; - for (j = 0; j < iface->num_bss; j++) + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + for (j = 0; j < iface->num_bss; j++) { + wpa_printf(MSG_DEBUG, "%s: free hapd %p", + __func__, iface->bss[j]); os_free(iface->bss[j]); + } hostapd_cleanup_iface(iface); } -#ifdef HOSTAPD +/** + * hostapd_init - Allocate and initialize per-interface data + * @config_file: Path to the configuration file + * Returns: Pointer to the allocated interface data or %NULL on failure + * + * This function is used to allocate main data structures for per-interface + * data. The allocated data buffer will be freed by calling + * hostapd_cleanup_iface(). + */ +struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, + const char *config_file) +{ + struct hostapd_iface *hapd_iface = NULL; + struct hostapd_config *conf = NULL; + struct hostapd_data *hapd; + size_t i; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) + goto fail; + + hapd_iface->config_fname = os_strdup(config_file); + if (hapd_iface->config_fname == NULL) + goto fail; + + conf = interfaces->config_read_cb(hapd_iface->config_fname); + if (conf == NULL) + goto fail; + hapd_iface->conf = conf; + + hapd_iface->num_bss = conf->num_bss; + hapd_iface->bss = os_calloc(conf->num_bss, + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + goto fail; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + conf->bss[i]); + if (hapd == NULL) + goto fail; + hapd->msg_ctx = hapd; + } + + return hapd_iface; + +fail: + wpa_printf(MSG_ERROR, "Failed to set up interface with %s", + config_file); + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + os_free(hapd_iface->config_fname); + os_free(hapd_iface->bss); + wpa_printf(MSG_DEBUG, "%s: free iface %p", + __func__, hapd_iface); + os_free(hapd_iface); + } + return NULL; +} + + +static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return 1; + } + } + + return 0; +} + + +/** + * hostapd_interface_init_bss - Read configuration file and init BSS data + * + * This function is used to parse configuration file for a BSS. This BSS is + * added to an existing interface sharing the same radio (if any) or a new + * interface is created if this is the first interface on a radio. This + * allocate memory for the BSS. No actual driver operations are started. + * + * This is similar to hostapd_interface_init(), but for a case where the + * configuration is used to add a single BSS instead of all BSSes for a radio. + */ +struct hostapd_iface * +hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, + const char *config_fname, int debug) +{ + struct hostapd_iface *new_iface = NULL, *iface = NULL; + struct hostapd_data *hapd; + int k; + size_t i, bss_idx; + + if (!phy || !*phy) + return NULL; + + for (i = 0; i < interfaces->count; i++) { + if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) { + iface = interfaces->iface[i]; + break; + } + } + + wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s", + config_fname, phy, iface ? "" : " --> new PHY"); + if (iface) { + struct hostapd_config *conf; + struct hostapd_bss_config **tmp_conf; + struct hostapd_data **tmp_bss; + struct hostapd_bss_config *bss; + const char *ifname; + + /* Add new BSS to existing iface */ + conf = interfaces->config_read_cb(config_fname); + if (conf == NULL) + return NULL; + if (conf->num_bss > 1) { + wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config"); + hostapd_config_free(conf); + return NULL; + } + + ifname = conf->bss[0]->iface; + if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) { + wpa_printf(MSG_ERROR, + "Interface name %s already in use", ifname); + hostapd_config_free(conf); + return NULL; + } + + tmp_conf = os_realloc_array( + iface->conf->bss, iface->conf->num_bss + 1, + sizeof(struct hostapd_bss_config *)); + tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1, + sizeof(struct hostapd_data *)); + if (tmp_bss) + iface->bss = tmp_bss; + if (tmp_conf) { + iface->conf->bss = tmp_conf; + iface->conf->last_bss = tmp_conf[0]; + } + if (tmp_bss == NULL || tmp_conf == NULL) { + hostapd_config_free(conf); + return NULL; + } + bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0]; + iface->conf->num_bss++; + + hapd = hostapd_alloc_bss_data(iface, iface->conf, bss); + if (hapd == NULL) { + iface->conf->num_bss--; + hostapd_config_free(conf); + return NULL; + } + iface->conf->last_bss = bss; + iface->bss[iface->num_bss] = hapd; + hapd->msg_ctx = hapd; + + bss_idx = iface->num_bss++; + conf->num_bss--; + conf->bss[0] = NULL; + hostapd_config_free(conf); + } else { + /* Add a new iface with the first BSS */ + new_iface = iface = hostapd_init(interfaces, config_fname); + if (!iface) + return NULL; + os_strlcpy(iface->phy, phy, sizeof(iface->phy)); + iface->interfaces = interfaces; + bss_idx = 0; + } + + for (k = 0; k < debug; k++) { + if (iface->bss[bss_idx]->conf->logger_stdout_level > 0) + iface->bss[bss_idx]->conf->logger_stdout_level--; + } + + if (iface->conf->bss[bss_idx]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[bss_idx])) { + wpa_printf(MSG_ERROR, "Interface name not specified in %s", + config_fname); + if (new_iface) + hostapd_interface_deinit_free(new_iface); + return NULL; + } + + return iface; +} + void hostapd_interface_deinit_free(struct hostapd_iface *iface) { const struct wpa_driver_ops *driver; void *drv_priv; + + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); if (iface == NULL) return; + wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u", + __func__, (unsigned int) iface->num_bss, + (unsigned int) iface->conf->num_bss); driver = iface->bss[0]->driver; drv_priv = iface->bss[0]->drv_priv; hostapd_interface_deinit(iface); - if (driver && driver->hapd_deinit && drv_priv) + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) { driver->hapd_deinit(drv_priv); + iface->bss[0]->drv_priv = NULL; + } hostapd_interface_free(iface); } +static void hostapd_deinit_driver(const struct wpa_driver_ops *driver, + void *drv_priv, + struct hostapd_iface *hapd_iface) +{ + size_t j; + + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + for (j = 0; j < hapd_iface->num_bss; j++) { + wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p", + __func__, (int) j, + hapd_iface->bss[j]->drv_priv); + if (hapd_iface->bss[j]->drv_priv == drv_priv) + hapd_iface->bss[j]->drv_priv = NULL; + } + } +} + + int hostapd_enable_iface(struct hostapd_iface *hapd_iface) { + size_t j; + if (hapd_iface->bss[0]->drv_priv != NULL) { wpa_printf(MSG_ERROR, "Interface %s already enabled", - hapd_iface->conf->bss[0].iface); + hapd_iface->conf->bss[0]->iface); return -1; } wpa_printf(MSG_DEBUG, "Enable interface %s", - hapd_iface->conf->bss[0].iface); + hapd_iface->conf->bss[0]->iface); + + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_set_security_params(hapd_iface->conf->bss[j], 1); + if (hostapd_config_check(hapd_iface->conf, 1) < 0) { + wpa_printf(MSG_INFO, "Invalid configuration - cannot enable"); + return -1; + } if (hapd_iface->interfaces == NULL || hapd_iface->interfaces->driver_init == NULL || - hapd_iface->interfaces->driver_init(hapd_iface) || - hostapd_setup_interface(hapd_iface)) { - hostapd_interface_deinit_free(hapd_iface); + hapd_iface->interfaces->driver_init(hapd_iface)) + return -1; + + if (hostapd_setup_interface(hapd_iface)) { + hostapd_deinit_driver(hapd_iface->bss[0]->driver, + hapd_iface->bss[0]->drv_priv, + hapd_iface); return -1; } + return 0; } @@ -1098,19 +1944,17 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface) size_t j; wpa_printf(MSG_DEBUG, "Reload interface %s", - hapd_iface->conf->bss[0].iface); - for (j = 0; j < hapd_iface->num_bss; j++) { - hostapd_flush_old_stations(hapd_iface->bss[j], - WLAN_REASON_PREV_AUTH_NOT_VALID); - -#ifndef CONFIG_NO_RADIUS - /* TODO: update dynamic data based on changed configuration - * items (e.g., open/close sockets, etc.) */ - radius_client_flush(hapd_iface->bss[j]->radius, 0); -#endif /* CONFIG_NO_RADIUS */ - - hostapd_reload_bss(hapd_iface->bss[j]); + hapd_iface->conf->bss[0]->iface); + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_set_security_params(hapd_iface->conf->bss[j], 1); + if (hostapd_config_check(hapd_iface->conf, 1) < 0) { + wpa_printf(MSG_ERROR, "Updated configuration is invalid"); + return -1; } + hostapd_clear_old(hapd_iface); + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_reload_bss(hapd_iface->bss[j]); + return 0; } @@ -1118,39 +1962,43 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface) int hostapd_disable_iface(struct hostapd_iface *hapd_iface) { size_t j; - struct hostapd_bss_config *bss; const struct wpa_driver_ops *driver; void *drv_priv; if (hapd_iface == NULL) return -1; - bss = hapd_iface->bss[0]->conf; + + if (hapd_iface->bss[0]->drv_priv == NULL) { + wpa_printf(MSG_INFO, "Interface %s already disabled", + hapd_iface->conf->bss[0]->iface); + return -1; + } + + wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); driver = hapd_iface->bss[0]->driver; drv_priv = hapd_iface->bss[0]->drv_priv; - /* whatever hostapd_interface_deinit does */ + hapd_iface->driver_ap_teardown = + !!(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + + /* same as hostapd_interface_deinit without deinitializing ctrl-iface */ for (j = 0; j < hapd_iface->num_bss; j++) { struct hostapd_data *hapd = hapd_iface->bss[j]; - hostapd_free_stas(hapd); - hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); - hostapd_clear_wep(hapd); + hostapd_bss_deinit_no_free(hapd); hostapd_free_hapd_data(hapd); } - if (driver && driver->hapd_deinit && drv_priv) { - driver->hapd_deinit(drv_priv); - hapd_iface->bss[0]->drv_priv = NULL; - } + hostapd_deinit_driver(driver, drv_priv, hapd_iface); /* From hostapd_cleanup_iface: These were initialized in * hostapd_setup_interface and hostapd_setup_interface_complete */ hostapd_cleanup_iface_partial(hapd_iface); - bss->wpa = 0; - bss->wpa_key_mgmt = -1; - bss->wpa_pairwise = -1; - wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface); + wpa_printf(MSG_DEBUG, "Interface %s disabled", + hapd_iface->bss[0]->conf->iface); + hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED); return 0; } @@ -1201,7 +2049,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, return NULL; } - bss = conf->last_bss = conf->bss; + bss = conf->last_bss = conf->bss[0]; os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); bss->ctrl_interface = os_strdup(ctrl_iface); @@ -1218,51 +2066,129 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, } -static struct hostapd_iface * hostapd_data_alloc( - struct hapd_interfaces *interfaces, struct hostapd_config *conf) +static int hostapd_data_alloc(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf) { size_t i; - struct hostapd_iface *hapd_iface = - interfaces->iface[interfaces->count - 1]; struct hostapd_data *hapd; - hapd_iface->conf = conf; - hapd_iface->num_bss = conf->num_bss; - - hapd_iface->bss = os_zalloc(conf->num_bss * + hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) - return NULL; + return -1; for (i = 0; i < conf->num_bss; i++) { hapd = hapd_iface->bss[i] = - hostapd_alloc_bss_data(hapd_iface, conf, - &conf->bss[i]); - if (hapd == NULL) - return NULL; + hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); + if (hapd == NULL) { + while (i > 0) { + i--; + os_free(hapd_iface->bss[i]); + hapd_iface->bss[i] = NULL; + } + os_free(hapd_iface->bss); + hapd_iface->bss = NULL; + return -1; + } hapd->msg_ctx = hapd; } - hapd_iface->interfaces = interfaces; + hapd_iface->conf = conf; + hapd_iface->num_bss = conf->num_bss; - return hapd_iface; + return 0; } int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) { struct hostapd_config *conf = NULL; - struct hostapd_iface *hapd_iface = NULL; + struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL; + struct hostapd_data *hapd; char *ptr; - size_t i; + size_t i, j; + const char *conf_file = NULL, *phy_name = NULL; + + if (os_strncmp(buf, "bss_config=", 11) == 0) { + char *pos; + phy_name = buf + 11; + pos = os_strchr(phy_name, ':'); + if (!pos) + return -1; + *pos++ = '\0'; + conf_file = pos; + if (!os_strlen(conf_file)) + return -1; + + hapd_iface = hostapd_interface_init_bss(interfaces, phy_name, + conf_file, 0); + if (!hapd_iface) + return -1; + for (j = 0; j < interfaces->count; j++) { + if (interfaces->iface[j] == hapd_iface) + break; + } + if (j == interfaces->count) { + struct hostapd_iface **tmp; + tmp = os_realloc_array(interfaces->iface, + interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (!tmp) { + hostapd_interface_deinit_free(hapd_iface); + return -1; + } + interfaces->iface = tmp; + interfaces->iface[interfaces->count++] = hapd_iface; + new_iface = hapd_iface; + } + + if (new_iface) { + if (interfaces->driver_init(hapd_iface)) + goto fail; + + if (hostapd_setup_interface(hapd_iface)) { + hostapd_deinit_driver( + hapd_iface->bss[0]->driver, + hapd_iface->bss[0]->drv_priv, + hapd_iface); + goto fail; + } + } else { + /* Assign new BSS with bss[0]'s driver info */ + hapd = hapd_iface->bss[hapd_iface->num_bss - 1]; + hapd->driver = hapd_iface->bss[0]->driver; + hapd->drv_priv = hapd_iface->bss[0]->drv_priv; + os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr, + ETH_ALEN); + + if (start_ctrl_iface_bss(hapd) < 0 || + (hapd_iface->state == HAPD_IFACE_ENABLED && + hostapd_setup_bss(hapd, -1))) { + hostapd_cleanup(hapd); + hapd_iface->bss[hapd_iface->num_bss - 1] = NULL; + hapd_iface->conf->num_bss--; + hapd_iface->num_bss--; + wpa_printf(MSG_DEBUG, "%s: free hapd %p %s", + __func__, hapd, hapd->conf->iface); + hostapd_config_free_bss(hapd->conf); + hapd->conf = NULL; + os_free(hapd); + return -1; + } + } + return 0; + } ptr = os_strchr(buf, ' '); if (ptr == NULL) return -1; *ptr++ = '\0'; + if (os_strncmp(ptr, "config=", 7) == 0) + conf_file = ptr + 7; + for (i = 0; i < interfaces->count; i++) { - if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface, + if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface, buf)) { wpa_printf(MSG_INFO, "Cannot add interface - it " "already exists"); @@ -1276,29 +2202,33 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) "for interface", __func__); goto fail; } + new_iface = hapd_iface; - conf = hostapd_config_alloc(interfaces, buf, ptr); - if (conf == NULL) { + if (conf_file && interfaces->config_read_cb) { + conf = interfaces->config_read_cb(conf_file); + if (conf && conf->bss) + os_strlcpy(conf->bss[0]->iface, buf, + sizeof(conf->bss[0]->iface)); + } else + conf = hostapd_config_alloc(interfaces, buf, ptr); + if (conf == NULL || conf->bss == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for configuration", __func__); goto fail; } - hapd_iface = hostapd_data_alloc(interfaces, conf); - if (hapd_iface == NULL) { + if (hostapd_data_alloc(hapd_iface, conf) < 0) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " "for hostapd", __func__); goto fail; } + conf = NULL; - if (hapd_iface->interfaces && - hapd_iface->interfaces->ctrl_iface_init && - hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) { - wpa_printf(MSG_ERROR, "%s: Failed to setup control " - "interface", __func__); + if (start_ctrl_iface(hapd_iface) < 0) goto fail; - } - wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface); + + wpa_printf(MSG_INFO, "Add interface '%s'", + hapd_iface->conf->bss[0]->iface); return 0; @@ -1306,24 +2236,84 @@ fail: if (conf) hostapd_config_free(conf); if (hapd_iface) { - os_free(hapd_iface->bss[interfaces->count]); - os_free(hapd_iface); + if (hapd_iface->bss) { + for (i = 0; i < hapd_iface->num_bss; i++) { + hapd = hapd_iface->bss[i]; + if (!hapd) + continue; + if (hapd_iface->interfaces && + hapd_iface->interfaces->ctrl_iface_deinit) + hapd_iface->interfaces-> + ctrl_iface_deinit(hapd); + wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", + __func__, hapd_iface->bss[i], + hapd->conf->iface); + hostapd_cleanup(hapd); + os_free(hapd); + hapd_iface->bss[i] = NULL; + } + os_free(hapd_iface->bss); + hapd_iface->bss = NULL; + } + if (new_iface) { + interfaces->count--; + interfaces->iface[interfaces->count] = NULL; + } + hostapd_cleanup_iface(hapd_iface); } return -1; } +static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx) +{ + size_t i; + + wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface); + + /* Remove hostapd_data only if it has already been initialized */ + if (idx < iface->num_bss) { + struct hostapd_data *hapd = iface->bss[idx]; + + hostapd_bss_deinit(hapd); + wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", + __func__, hapd, hapd->conf->iface); + hostapd_config_free_bss(hapd->conf); + hapd->conf = NULL; + os_free(hapd); + + iface->num_bss--; + + for (i = idx; i < iface->num_bss; i++) + iface->bss[i] = iface->bss[i + 1]; + } else { + hostapd_config_free_bss(iface->conf->bss[idx]); + iface->conf->bss[idx] = NULL; + } + + iface->conf->num_bss--; + for (i = idx; i < iface->conf->num_bss; i++) + iface->conf->bss[i] = iface->conf->bss[i + 1]; + + return 0; +} + + int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) { struct hostapd_iface *hapd_iface; - size_t i, k = 0; + size_t i, j, k = 0; for (i = 0; i < interfaces->count; i++) { hapd_iface = interfaces->iface[i]; if (hapd_iface == NULL) return -1; - if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) { + if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) { wpa_printf(MSG_INFO, "Remove interface '%s'", buf); + hapd_iface->driver_ap_teardown = + !!(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + hostapd_interface_deinit_free(hapd_iface); k = i; while (k < (interfaces->count - 1)) { @@ -1334,12 +2324,19 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) interfaces->count--; return 0; } + + for (j = 0; j < hapd_iface->conf->num_bss; j++) { + if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) { + hapd_iface->driver_ap_teardown = + !(hapd_iface->drv_flags & + WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + return hostapd_remove_bss(hapd_iface, j); + } + } } return -1; } -#endif /* HOSTAPD */ - /** * hostapd_new_assoc_sta - Notify that a new station associated with the AP @@ -1379,8 +2376,9 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, /* Start accounting here, if IEEE 802.1X and WPA are not used. * IEEE 802.1X/WPA code will start accounting after the station has * been authorized. */ - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) { - os_get_time(&sta->connected_time); + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) { + ap_sta_set_authorized(hapd, sta, 1); + os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); } @@ -1393,11 +2391,335 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " - "for " MACSTR " (%d seconds - ap_max_inactivity)", - __func__, MAC2STR(sta->addr), - hapd->conf->ap_max_inactivity); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, - ap_handle_timer, hapd, sta); + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(sta->addr), + hapd->conf->ap_max_inactivity); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + } +} + + +const char * hostapd_state_text(enum hostapd_iface_state s) +{ + switch (s) { + case HAPD_IFACE_UNINITIALIZED: + return "UNINITIALIZED"; + case HAPD_IFACE_DISABLED: + return "DISABLED"; + case HAPD_IFACE_COUNTRY_UPDATE: + return "COUNTRY_UPDATE"; + case HAPD_IFACE_ACS: + return "ACS"; + case HAPD_IFACE_HT_SCAN: + return "HT_SCAN"; + case HAPD_IFACE_DFS: + return "DFS"; + case HAPD_IFACE_ENABLED: + return "ENABLED"; + } + + return "UNKNOWN"; } + + +void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) +{ + wpa_printf(MSG_INFO, "%s: interface state %s->%s", + iface->conf->bss[0]->iface, hostapd_state_text(iface->state), + hostapd_state_text(s)); + iface->state = s; +} + + +#ifdef NEED_AP_MLME + +static void free_beacon_data(struct beacon_data *beacon) +{ + os_free(beacon->head); + beacon->head = NULL; + os_free(beacon->tail); + beacon->tail = NULL; + os_free(beacon->probe_resp); + beacon->probe_resp = NULL; + os_free(beacon->beacon_ies); + beacon->beacon_ies = NULL; + os_free(beacon->proberesp_ies); + beacon->proberesp_ies = NULL; + os_free(beacon->assocresp_ies); + beacon->assocresp_ies = NULL; +} + + +static int hostapd_build_beacon_data(struct hostapd_data *hapd, + struct beacon_data *beacon) +{ + struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra; + struct wpa_driver_ap_params params; + int ret; + + os_memset(beacon, 0, sizeof(*beacon)); + ret = ieee802_11_build_ap_params(hapd, ¶ms); + if (ret < 0) + return ret; + + ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra, + &proberesp_extra, + &assocresp_extra); + if (ret) + goto free_ap_params; + + ret = -1; + beacon->head = os_malloc(params.head_len); + if (!beacon->head) + goto free_ap_extra_ies; + + os_memcpy(beacon->head, params.head, params.head_len); + beacon->head_len = params.head_len; + + beacon->tail = os_malloc(params.tail_len); + if (!beacon->tail) + goto free_beacon; + + os_memcpy(beacon->tail, params.tail, params.tail_len); + beacon->tail_len = params.tail_len; + + if (params.proberesp != NULL) { + beacon->probe_resp = os_malloc(params.proberesp_len); + if (!beacon->probe_resp) + goto free_beacon; + + os_memcpy(beacon->probe_resp, params.proberesp, + params.proberesp_len); + beacon->probe_resp_len = params.proberesp_len; + } + + /* copy the extra ies */ + if (beacon_extra) { + beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); + if (!beacon->beacon_ies) + goto free_beacon; + + os_memcpy(beacon->beacon_ies, + beacon_extra->buf, wpabuf_len(beacon_extra)); + beacon->beacon_ies_len = wpabuf_len(beacon_extra); + } + + if (proberesp_extra) { + beacon->proberesp_ies = + os_malloc(wpabuf_len(proberesp_extra)); + if (!beacon->proberesp_ies) + goto free_beacon; + + os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, + wpabuf_len(proberesp_extra)); + beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); + } + + if (assocresp_extra) { + beacon->assocresp_ies = + os_malloc(wpabuf_len(assocresp_extra)); + if (!beacon->assocresp_ies) + goto free_beacon; + + os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, + wpabuf_len(assocresp_extra)); + beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); + } + + ret = 0; +free_beacon: + /* if the function fails, the caller should not free beacon data */ + if (ret) + free_beacon_data(beacon); + +free_ap_extra_ies: + hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra, + assocresp_extra); +free_ap_params: + ieee802_11_free_ap_params(¶ms); + return ret; +} + + +/* + * TODO: This flow currently supports only changing frequency within the + * same hw_mode. Any other changes to MAC parameters or provided settings (even + * width) are not supported. + */ +static int hostapd_change_config_freq(struct hostapd_data *hapd, + struct hostapd_config *conf, + struct hostapd_freq_params *params, + struct hostapd_freq_params *old_params) +{ + int channel; + + if (!params->channel) { + /* check if the new channel is supported by hw */ + params->channel = hostapd_hw_get_channel(hapd, params->freq); + } + + channel = params->channel; + if (!channel) + return -1; + + /* if a pointer to old_params is provided we save previous state */ + if (old_params) { + old_params->channel = conf->channel; + old_params->ht_enabled = conf->ieee80211n; + old_params->sec_channel_offset = conf->secondary_channel; + } + + conf->channel = channel; + conf->ieee80211n = params->ht_enabled; + conf->secondary_channel = params->sec_channel_offset; + + /* TODO: maybe call here hostapd_config_check here? */ + + return 0; +} + + +static int hostapd_fill_csa_settings(struct hostapd_data *hapd, + struct csa_settings *settings) +{ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_freq_params old_freq; + int ret; + + os_memset(&old_freq, 0, sizeof(old_freq)); + if (!iface || !iface->freq || hapd->csa_in_progress) + return -1; + + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, + &settings->freq_params, + &old_freq); + if (ret) + return ret; + + ret = hostapd_build_beacon_data(hapd, &settings->beacon_after); + + /* change back the configuration */ + hostapd_change_config_freq(iface->bss[0], iface->conf, + &old_freq, NULL); + + if (ret) + return ret; + + /* set channel switch parameters for csa ie */ + hapd->cs_freq_params = settings->freq_params; + hapd->cs_count = settings->cs_count; + hapd->cs_block_tx = settings->block_tx; + + ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa); + if (ret) { + free_beacon_data(&settings->beacon_after); + return ret; + } + + settings->counter_offset_beacon = hapd->cs_c_off_beacon; + settings->counter_offset_presp = hapd->cs_c_off_proberesp; + + return 0; +} + + +void hostapd_cleanup_cs_params(struct hostapd_data *hapd) +{ + os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params)); + hapd->cs_count = 0; + hapd->cs_block_tx = 0; + hapd->cs_c_off_beacon = 0; + hapd->cs_c_off_proberesp = 0; + hapd->csa_in_progress = 0; +} + + +int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) +{ + int ret; + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_printf(MSG_INFO, "CSA is not supported"); + return -1; + } + + ret = hostapd_fill_csa_settings(hapd, settings); + if (ret) + return ret; + + ret = hostapd_drv_switch_channel(hapd, settings); + free_beacon_data(&settings->beacon_csa); + free_beacon_data(&settings->beacon_after); + + if (ret) { + /* if we failed, clean cs parameters */ + hostapd_cleanup_cs_params(hapd); + return ret; + } + + hapd->csa_in_progress = 1; + return 0; +} + + +void +hostapd_switch_channel_fallback(struct hostapd_iface *iface, + const struct hostapd_freq_params *freq_params) +{ + int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT; + unsigned int i; + + wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); + + if (freq_params->center_freq1) + vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5; + if (freq_params->center_freq2) + vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5; + + switch (freq_params->bandwidth) { + case 0: + case 20: + case 40: + vht_bw = VHT_CHANWIDTH_USE_HT; + break; + case 80: + if (freq_params->center_freq2) + vht_bw = VHT_CHANWIDTH_80P80MHZ; + else + vht_bw = VHT_CHANWIDTH_80MHZ; + break; + case 160: + vht_bw = VHT_CHANWIDTH_160MHZ; + break; + default: + wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d", + freq_params->bandwidth); + break; + } + + iface->freq = freq_params->freq; + iface->conf->channel = freq_params->channel; + iface->conf->secondary_channel = freq_params->sec_channel_offset; + iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx; + iface->conf->vht_oper_chwidth = vht_bw; + iface->conf->ieee80211n = freq_params->ht_enabled; + iface->conf->ieee80211ac = freq_params->vht_enabled; + + /* + * cs_params must not be cleared earlier because the freq_params + * argument may actually point to one of these. + */ + for (i = 0; i < iface->num_bss; i++) + hostapd_cleanup_cs_params(iface->bss[i]); + + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); +} + +#endif /* NEED_AP_MLME */ diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index f1e7d9ff7ec5..75cc24edb665 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,19 +10,22 @@ #define HOSTAPD_H #include "common/defs.h" +#include "utils/list.h" #include "ap_config.h" +#include "drivers/driver.h" -struct wpa_driver_ops; struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; struct hostapd_data; struct sta_info; -struct hostap_sta_driver_data; 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; @@ -40,9 +43,19 @@ struct hapd_interfaces { int global_ctrl_sock; 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; }; +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, @@ -63,6 +76,25 @@ struct hostapd_frame_info { 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 @@ -72,6 +104,9 @@ struct hostapd_data { 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]; @@ -111,7 +146,7 @@ struct hostapd_data { struct eapol_authenticator *eapol_auth; struct rsn_preauth_interface *preauth_iface; - time_t michael_mic_failure; + struct os_reltime michael_mic_failure; int michael_mic_failures; int tkip_countermeasures; @@ -121,6 +156,7 @@ struct hostapd_data { 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; @@ -143,6 +179,8 @@ struct hostapd_data { 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; @@ -151,6 +189,9 @@ struct hostapd_data { 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); @@ -171,6 +212,22 @@ struct hostapd_data { 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; @@ -188,10 +245,34 @@ struct hostapd_data { #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; +#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 */ }; @@ -203,16 +284,42 @@ struct hostapd_iface { 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; + + /* + * 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]; - struct ap_info *ap_iter_list; - unsigned int drv_flags; + 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 @@ -220,6 +327,12 @@ struct hostapd_iface { */ 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; @@ -253,11 +366,40 @@ struct hostapd_iface { /* 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; }; /* hostapd.c */ @@ -273,6 +415,11 @@ 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); @@ -281,6 +428,15 @@ 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); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -296,11 +452,13 @@ 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 offset, int width, int cf1, int cf2); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, diff --git a/src/ap/hs20.c b/src/ap/hs20.c index 45d518bc1e55..d7909fad4a14 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -1,7 +1,7 @@ /* * Hotspot 2.0 AP ANQP processing * Copyright (c) 2009, Atheros Communications, Inc. - * Copyright (c) 2011-2012, Qualcomm Atheros, 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. @@ -13,19 +13,165 @@ #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++ = 5; + *eid++ = 7; WPA_PUT_BE24(eid, OUI_WFA); eid += 3; *eid++ = HS20_INDICATION_OUI_TYPE; - /* Hotspot Configuration: DGAF Enabled */ - *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00; + 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/src/ap/hs20.h b/src/ap/hs20.h index 98698ce2fd66..152439f4dcb4 100644 --- a/src/ap/hs20.h +++ b/src/ap/hs20.h @@ -1,6 +1,6 @@ /* * Hotspot 2.0 AP ANQP processing - * Copyright (c) 2011-2012, Qualcomm Atheros, 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. @@ -12,5 +12,11 @@ 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/src/ap/hw_features.c b/src/ap/hw_features.c index 923b69823070..05431d32c205 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -4,14 +4,8 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,10 +14,14 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" -#include "drivers/driver.h" +#include "common/wpa_ctrl.h" +#include "common/hw_features_common.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "acs.h" +#include "ieee802_11.h" +#include "beacon.h" #include "hw_features.h" @@ -44,10 +42,40 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, } +#ifndef CONFIG_NO_STDOUT_DEBUG +static char * dfs_info(struct hostapd_channel_data *chan) +{ + static char info[256]; + char *state; + + switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { + case HOSTAPD_CHAN_DFS_UNKNOWN: + state = "unknown"; + break; + case HOSTAPD_CHAN_DFS_USABLE: + state = "usable"; + break; + case HOSTAPD_CHAN_DFS_UNAVAILABLE: + state = "unavailable"; + break; + case HOSTAPD_CHAN_DFS_AVAILABLE: + state = "available"; + break; + default: + return ""; + } + os_snprintf(info, sizeof(info), " (DFS state = %s)", state); + info[sizeof(info) - 1] = '\0'; + + return info; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + int hostapd_get_hw_features(struct hostapd_iface *iface) { struct hostapd_data *hapd = iface->bss[0]; - int ret = 0, i, j; + int i, j; u16 num_modes, flags; struct hostapd_hw_modes *modes; @@ -70,34 +98,47 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) for (i = 0; i < num_modes; i++) { struct hostapd_hw_modes *feature = &modes[i]; + int dfs_enabled = hapd->iconf->ieee80211h && + (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); + /* set flag for channels we can use in current regulatory * domain */ for (j = 0; j < feature->num_channels; j++) { + int dfs = 0; + /* * Disable all channels that are marked not to allow - * IBSS operation or active scanning. In addition, - * disable all channels that require radar detection, - * since that (in addition to full DFS) is not yet - * supported. + * to initiate radiation (a.k.a. passive scan and no + * IBSS). + * Use radar channels only if the driver supports DFS. */ - if (feature->channels[j].flag & - (HOSTAPD_CHAN_NO_IBSS | - HOSTAPD_CHAN_PASSIVE_SCAN | - HOSTAPD_CHAN_RADAR)) + if ((feature->channels[j].flag & + HOSTAPD_CHAN_RADAR) && dfs_enabled) { + dfs = 1; + } else if (((feature->channels[j].flag & + HOSTAPD_CHAN_RADAR) && + !(iface->drv_flags & + WPA_DRIVER_FLAGS_DFS_OFFLOAD)) || + (feature->channels[j].flag & + HOSTAPD_CHAN_NO_IR)) { feature->channels[j].flag |= HOSTAPD_CHAN_DISABLED; + } + if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; + wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " - "chan=%d freq=%d MHz max_tx_power=%d dBm", + "chan=%d freq=%d MHz max_tx_power=%d dBm%s", feature->mode, feature->channels[j].chan, feature->channels[j].freq, - feature->channels[j].max_tx_power); + feature->channels[j].max_tx_power, + dfs ? dfs_info(&feature->channels[j]) : ""); } } - return ret; + return 0; } @@ -183,66 +224,16 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, #ifdef CONFIG_IEEE80211N static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) { - int sec_chan, ok, j, first; - int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, - 184, 192 }; - size_t k; + int pri_chan, sec_chan; if (!iface->conf->secondary_channel) return 1; /* HT40 not used */ - sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; - wpa_printf(MSG_DEBUG, "HT40: control channel: %d " - "secondary channel: %d", - iface->conf->channel, sec_chan); - - /* Verify that HT40 secondary channel is an allowed 20 MHz - * channel */ - ok = 0; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_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 (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) - return 1; - - if (iface->conf->secondary_channel > 0) - first = iface->conf->channel; - else - first = sec_chan; - - ok = 0; - for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { - if (first == allowed[k]) { - ok = 1; - break; - } - } - if (!ok) { - wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", - iface->conf->channel, - iface->conf->secondary_channel); - return 0; - } + pri_chan = iface->conf->channel; + sec_chan = pri_chan + iface->conf->secondary_channel * 4; - return 1; + return allowed_ht40_channel_pair(iface->current_mode, pri_chan, + sec_chan); } @@ -258,158 +249,34 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) } -static void ieee80211n_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 && - elems.ht_operation_len >= sizeof(*oper)) { - oper = (struct ieee80211_ht_operation *) elems.ht_operation; - *pri_chan = oper->control_chan; - if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_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; - } - } -} - - static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, struct wpa_scan_results *scan_res) { - int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss; - int bss_pri_chan, bss_sec_chan; - size_t i; - int match; + int pri_chan, sec_chan; + int res; pri_chan = iface->conf->channel; - sec_chan = iface->conf->secondary_channel * 4; - pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan); - if (iface->conf->secondary_channel > 0) - sec_freq = pri_freq + 20; - else - sec_freq = pri_freq - 20; + sec_chan = pri_chan + iface->conf->secondary_channel * 4; - /* - * 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"); - ieee80211n_switch_pri_sec(iface); - } + res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan); - /* - * 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]; - ieee80211n_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]; - ieee80211n_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)); - ieee80211n_switch_pri_sec(iface); - break; - } - } - } + if (res == 2) + ieee80211n_switch_pri_sec(iface); - return 1; + return !!res; } static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, struct wpa_scan_results *scan_res) { - int pri_freq, sec_freq; - int affected_start, affected_end; - size_t i; + int pri_chan, sec_chan; - pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); - if (iface->conf->secondary_channel > 0) - sec_freq = pri_freq + 20; - else - sec_freq = pri_freq - 20; - 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; - int sec_chan, pri_chan; - - ieee80211n_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; - } - } - - /* TODO: 40 MHz intolerant */ - } + pri_chan = iface->conf->channel; + sec_chan = pri_chan + iface->conf->secondary_channel * 4; - return 1; + return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan, + sec_chan); } @@ -436,6 +303,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); wpa_scan_results_free(scan_res); + iface->secondary_ch = iface->conf->secondary_channel; if (!oper40) { wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " "channel pri=%d sec=%d based on overlapping BSSes", @@ -443,10 +311,21 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) iface->conf->channel + iface->conf->secondary_channel * 4); iface->conf->secondary_channel = 0; - iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) { + /* + * TODO: Could consider scheduling another scan to check + * if channel width can be changed if no coex reports + * are received from associating stations. + */ + } } res = ieee80211n_allowed_ht40_channel_pair(iface); + if (!res) { + iface->conf->secondary_channel = 0; + wpa_printf(MSG_INFO, "Fallback to 20 MHz"); + } + hostapd_setup_interface_complete(iface, !res); } @@ -491,25 +370,128 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, } +static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, + struct wpa_driver_scan_params *params) +{ + /* Scan only the affected frequency range */ + int pri_freq; + int affected_start, affected_end; + int i, pos; + struct hostapd_hw_modes *mode; + + if (iface->current_mode == NULL) + return; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + if (iface->conf->secondary_channel > 0) { + affected_start = pri_freq - 10; + affected_end = pri_freq + 30; + } else { + affected_start = pri_freq - 30; + affected_end = pri_freq + 10; + } + wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + + mode = iface->current_mode; + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + pos = 0; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (chan->freq < affected_start || + chan->freq > affected_end) + continue; + params->freqs[pos++] = chan->freq; + } +} + + +static void ap_ht40_scan_retry(void *eloop_data, void *user_data) +{ +#define HT2040_COEX_SCAN_RETRY 15 + struct hostapd_iface *iface = eloop_data; + struct wpa_driver_scan_params params; + int ret; + + os_memset(¶ms, 0, sizeof(params)); + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + ieee80211n_scan_channels_2g4(iface, ¶ms); + else + ieee80211n_scan_channels_5g(iface, ¶ms); + + ret = hostapd_driver_scan(iface->bss[0], ¶ms); + iface->num_ht40_scan_tries++; + os_free(params.freqs); + + if (ret == -EBUSY && + iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) { + wpa_printf(MSG_ERROR, + "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)", + ret, strerror(-ret), iface->num_ht40_scan_tries); + eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); + return; + } + + if (ret == 0) { + iface->scan_cb = ieee80211n_check_scan; + return; + } + + wpa_printf(MSG_DEBUG, + "Failed to request a scan in device, bringing up in HT20 mode"); + iface->conf->secondary_channel = 0; + iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + hostapd_setup_interface_complete(iface, 0); +} + + +void hostapd_stop_setup_timers(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); +} + + static int ieee80211n_check_40mhz(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; + int ret; if (!iface->conf->secondary_channel) return 0; /* HT40 not used */ + hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " "40 MHz channel"); os_memset(¶ms, 0, sizeof(params)); if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) ieee80211n_scan_channels_2g4(iface, ¶ms); - if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { - wpa_printf(MSG_ERROR, "Failed to request a scan of " - "neighboring BSSes"); - os_free(params.freqs); + else + ieee80211n_scan_channels_5g(iface, ¶ms); + + ret = hostapd_driver_scan(iface->bss[0], ¶ms); + os_free(params.freqs); + + if (ret == -EBUSY) { + wpa_printf(MSG_ERROR, + "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again", + ret, strerror(-ret)); + iface->num_ht40_scan_tries = 1; + eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); + eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); + return 1; + } + + if (ret < 0) { + wpa_printf(MSG_ERROR, + "Failed to request a scan of neighboring BSSes ret=%d (%s)", + ret, strerror(-ret)); return -1; } - os_free(params.freqs); iface->scan_cb = ieee80211n_check_scan; return 1; @@ -535,11 +517,24 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && - (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [SMPS-*]"); - return 0; + switch (conf & HT_CAP_INFO_SMPS_MASK) { + case HT_CAP_INFO_SMPS_STATIC: + if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) { + wpa_printf(MSG_ERROR, + "Driver does not support configured HT capability [SMPS-STATIC]"); + return 0; + } + break; + case HT_CAP_INFO_SMPS_DYNAMIC: + if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) { + wpa_printf(MSG_ERROR, + "Driver does not support configured HT capability [SMPS-DYNAMIC]"); + return 0; + } + break; + case HT_CAP_INFO_SMPS_DISABLED: + default: + break; } if ((conf & HT_CAP_INFO_GREEN_FIELD) && @@ -597,12 +592,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [PSMP]"); - return 0; - } - if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { wpa_printf(MSG_ERROR, "Driver does not support configured " @@ -613,6 +602,112 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 1; } + +#ifdef CONFIG_IEEE80211AC + +static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) +{ + u32 req_cap = conf & cap; + + /* + * Make sure we support all requested capabilities. + * NOTE: We assume that 'cap' represents a capability mask, + * not a discrete value. + */ + if ((hw & req_cap) != req_cap) { + wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", + name); + return 0; + } + return 1; +} + + +static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, + unsigned int shift, + const char *name) +{ + u32 hw_max = hw & mask; + u32 conf_val = conf & mask; + + if (conf_val > hw_max) { + wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", + name, conf_val >> shift, hw_max >> shift); + return 0; + } + return 1; +} + + +static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode = iface->current_mode; + u32 hw = mode->vht_capab; + u32 conf = iface->conf->vht_capab; + + wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", + hw, conf); + + if (mode->mode == HOSTAPD_MODE_IEEE80211G && + iface->conf->bss[0]->vendor_vht && + mode->vht_capab == 0 && iface->hw_features) { + int i; + + for (i = 0; i < iface->num_hw_features; i++) { + if (iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) { + mode = &iface->hw_features[i]; + hw = mode->vht_capab; + wpa_printf(MSG_DEBUG, + "update hw vht capab based on 5 GHz band: 0x%x", + hw); + break; + } + } + } + +#define VHT_CAP_CHECK(cap) \ + do { \ + if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ + return 0; \ + } while (0) + +#define VHT_CAP_CHECK_MAX(cap) \ + do { \ + if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ + #cap)) \ + return 0; \ + } while (0) + + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); + VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); + VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); + VHT_CAP_CHECK(VHT_CAP_RXLDPC); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); + VHT_CAP_CHECK(VHT_CAP_TXSTBC); + VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); + VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); + VHT_CAP_CHECK(VHT_CAP_HTC_VHT); + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); + VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); + VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); + +#undef VHT_CAP_CHECK +#undef VHT_CAP_CHECK_MAX + + return 1; +} +#endif /* CONFIG_IEEE80211AC */ + #endif /* CONFIG_IEEE80211N */ @@ -624,6 +719,10 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) return 0; if (!ieee80211n_supported_ht_capab(iface)) return -1; +#ifdef CONFIG_IEEE80211AC + if (!ieee80211ac_supported_vht_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211AC */ ret = ieee80211n_check_40mhz(iface); if (ret) return ret; @@ -635,6 +734,129 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) } +static int hostapd_is_usable_chan(struct hostapd_iface *iface, + int channel, int primary) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->chan != channel) + continue; + + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) + return 1; + + wpa_printf(MSG_DEBUG, + "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s", + primary ? "" : "Configured HT40 secondary ", + i, chan->chan, chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); + } + + return 0; +} + + +static int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) + return 0; + + if (!iface->conf->secondary_channel) + return 1; + + return hostapd_is_usable_chan(iface, iface->conf->channel + + iface->conf->secondary_channel * 4, 0); +} + + +static enum hostapd_chan_status +hostapd_check_chans(struct hostapd_iface *iface) +{ + if (iface->conf->channel) { + if (hostapd_is_usable_chans(iface)) + return HOSTAPD_CHAN_VALID; + else + return HOSTAPD_CHAN_INVALID; + } + + /* + * The user set channel=0 or channel=acs_survey + * which is used to trigger ACS. + */ + + switch (acs_init(iface)) { + case HOSTAPD_CHAN_ACS: + return HOSTAPD_CHAN_ACS; + case HOSTAPD_CHAN_VALID: + case HOSTAPD_CHAN_INVALID: + default: + return HOSTAPD_CHAN_INVALID; + } +} + + +static void hostapd_notify_bad_chans(struct hostapd_iface *iface) +{ + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured channel (%d) not found from the " + "channel list of current mode (%d) %s", + iface->conf->channel, + iface->current_mode->mode, + hostapd_hw_mode_txt(iface->current_mode->mode)); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured channel"); +} + + +int hostapd_acs_completed(struct hostapd_iface *iface, int err) +{ + int ret = -1; + + if (err) + goto out; + + switch (hostapd_check_chans(iface)) { + case HOSTAPD_CHAN_VALID: + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + ACS_EVENT_COMPLETED "freq=%d channel=%d", + hostapd_hw_get_freq(iface->bss[0], + iface->conf->channel), + iface->conf->channel); + break; + case HOSTAPD_CHAN_ACS: + wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); + hostapd_notify_bad_chans(iface); + goto out; + case HOSTAPD_CHAN_INVALID: + default: + wpa_printf(MSG_ERROR, "ACS picked unusable channels"); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); + hostapd_notify_bad_chans(iface); + goto out; + } + + ret = hostapd_check_ht_capab(iface); + if (ret < 0) + goto out; + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback"); + return 0; + } + + ret = 0; +out: + return hostapd_setup_interface_complete(iface, ret); +} + + /** * hostapd_select_hw_mode - Select the hardware mode * @iface: Pointer to interface data. @@ -645,11 +867,20 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) */ int hostapd_select_hw_mode(struct hostapd_iface *iface) { - int i, j, ok; + int i; if (iface->num_hw_features < 1) return -1; + if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G || + iface->conf->ieee80211n || iface->conf->ieee80211ac) && + iface->conf->channel == 14) { + wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14"); + iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B; + iface->conf->ieee80211n = 0; + iface->conf->ieee80211ac = 0; + } + iface->current_mode = NULL; for (i = 0; i < iface->num_hw_features; i++) { struct hostapd_hw_modes *mode = &iface->hw_features[i]; @@ -670,82 +901,16 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) return -2; } - ok = 0; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if (chan->chan == iface->conf->channel) { - if (chan->flag & HOSTAPD_CHAN_DISABLED) { - wpa_printf(MSG_ERROR, - "channel [%i] (%i) is disabled for " - "use in AP mode, flags: 0x%x%s%s%s", - j, chan->chan, chan->flag, - chan->flag & HOSTAPD_CHAN_NO_IBSS ? - " NO-IBSS" : "", - chan->flag & - HOSTAPD_CHAN_PASSIVE_SCAN ? - " PASSIVE-SCAN" : "", - chan->flag & HOSTAPD_CHAN_RADAR ? - " RADAR" : ""); - } else { - ok = 1; - break; - } - } - } - if (ok && iface->conf->secondary_channel) { - int sec_ok = 0; - int sec_chan = iface->conf->channel + - iface->conf->secondary_channel * 4; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - (chan->chan == sec_chan)) { - sec_ok = 1; - break; - } - } - if (!sec_ok) { - hostapd_logger(iface->bss[0], NULL, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Configured HT40 secondary channel " - "(%d) not found from the channel list " - "of current mode (%d) %s", - sec_chan, iface->current_mode->mode, - hostapd_hw_mode_txt( - iface->current_mode->mode)); - ok = 0; - } - } - if (iface->conf->channel == 0) { - /* TODO: could request a scan of neighboring BSSes and select - * the channel automatically */ - wpa_printf(MSG_ERROR, "Channel not configured " - "(hw_mode/channel in hostapd.conf)"); + switch (hostapd_check_chans(iface)) { + case HOSTAPD_CHAN_VALID: + return 0; + case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */ + return 1; + case HOSTAPD_CHAN_INVALID: + default: + hostapd_notify_bad_chans(iface); return -3; } - if (ok == 0 && iface->conf->channel != 0) { - hostapd_logger(iface->bss[0], NULL, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Configured channel (%d) not found from the " - "channel list of current mode (%d) %s", - iface->conf->channel, - iface->current_mode->mode, - hostapd_hw_mode_txt(iface->current_mode->mode)); - iface->current_mode = NULL; - } - - if (iface->current_mode == NULL) { - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured channel"); - return -4; - } - - return 0; } @@ -768,35 +933,11 @@ const char * hostapd_hw_mode_txt(int mode) int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) { - int i; - - if (!hapd->iface->current_mode) - return 0; - - for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &hapd->iface->current_mode->channels[i]; - if (ch->chan == chan) - return ch->freq; - } - - return 0; + return hw_get_freq(hapd->iface->current_mode, chan); } int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) { - int i; - - if (!hapd->iface->current_mode) - return 0; - - for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &hapd->iface->current_mode->channels[i]; - if (ch->freq == freq) - return ch->chan; - } - - return 0; + return hw_get_chan(hapd->iface->current_mode, freq); } diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index abadcd137db1..0f67ab8e5f37 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -4,14 +4,8 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2008-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HW_FEATURES_H @@ -21,6 +15,7 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features); int hostapd_get_hw_features(struct hostapd_iface *iface); +int hostapd_acs_completed(struct hostapd_iface *iface, int err); int hostapd_select_hw_mode(struct hostapd_iface *iface); const char * hostapd_hw_mode_txt(int mode); int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); @@ -28,6 +23,7 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); int hostapd_check_ht_capab(struct hostapd_iface *iface); int hostapd_prepare_rates(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); +void hostapd_stop_setup_timers(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -66,6 +62,10 @@ static inline int hostapd_prepare_rates(struct hostapd_iface *iface, return 0; } +static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface) +{ +} + #endif /* NEED_AP_MLME */ #endif /* HW_FEATURES_H */ diff --git a/src/ap/iapp.c b/src/ap/iapp.c index be55c695672c..99aa04dc3dd9 100644 --- a/src/ap/iapp.c +++ b/src/ap/iapp.c @@ -204,7 +204,7 @@ static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) addr.sin_port = htons(IAPP_UDP_PORT); if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) - perror("sendto[IAPP-ADD]"); + wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno)); } @@ -231,7 +231,7 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) * FIX: what is correct RW with 802.11? */ if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) - perror("send[L2 Update]"); + wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno)); } @@ -242,29 +242,22 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) */ void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) { - struct ieee80211_mgmt *assoc; - u16 seq; + u16 seq = 0; /* TODO */ if (iapp == NULL) return; - assoc = sta->last_assoc_req; - seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0; - /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */ hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq); iapp_send_layer2_update(iapp, sta->addr); iapp_send_add(iapp, sta->addr, seq); - if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) == - WLAN_FC_STYPE_REASSOC_REQ) { - /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, - * Context Block, Timeout) - */ - /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to - * IP address */ - } + /* TODO: If this was reassociation: + * IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, + * Context Block, Timeout) + * TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to + * IP address */ } @@ -276,8 +269,8 @@ static void iapp_process_add_notify(struct iapp_data *iapp, struct sta_info *sta; if (len != sizeof(*add)) { - printf("Invalid IAPP-ADD packet length %d (expected %lu)\n", - len, (unsigned long) sizeof(*add)); + wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)", + len, (unsigned long) sizeof(*add)); return; } @@ -326,7 +319,8 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { - perror("recvfrom"); + wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s", + strerror(errno)); return; } @@ -350,23 +344,24 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) hdr->version, hdr->command, be_to_host16(hdr->identifier), hlen); if (hdr->version != IAPP_VERSION) { - printf("Dropping IAPP frame with unknown version %d\n", - hdr->version); + wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d", + hdr->version); return; } if (hlen > len) { - printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len); + wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)", + hlen, len); return; } if (hlen < len) { - printf("Ignoring %d extra bytes from IAPP frame\n", - len - hlen); + wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame", + len - hlen); len = hlen; } switch (hdr->command) { case IAPP_CMD_ADD_notify: - iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr)); + iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr)); break; case IAPP_CMD_MOVE_notify: /* TODO: MOVE is using TCP; so move this to TCP handler once it @@ -376,7 +371,7 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) /* TODO: process */ break; default: - printf("Unknown IAPP command %d\n", hdr->command); + wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command); break; } } @@ -403,7 +398,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); if (iapp->udp_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -411,35 +407,38 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } ifindex = ifr.ifr_ifindex; if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFADDR)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } paddr = (struct sockaddr_in *) &ifr.ifr_addr; if (paddr->sin_family != AF_INET) { - printf("Invalid address family %i (SIOCGIFADDR)\n", - paddr->sin_family); + wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)", + paddr->sin_family); iapp_deinit(iapp); return NULL; } iapp->own.s_addr = paddr->sin_addr.s_addr; if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFBRDADDR)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } paddr = (struct sockaddr_in *) &ifr.ifr_addr; if (paddr->sin_family != AF_INET) { - printf("Invalid address family %i (SIOCGIFBRDADDR)\n", - paddr->sin_family); + wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)", + paddr->sin_family); iapp_deinit(iapp); return NULL; } @@ -450,7 +449,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) uaddr.sin_port = htons(IAPP_UDP_PORT); if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { - perror("bind[UDP]"); + wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -461,14 +461,16 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); + wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (iapp->packet_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -478,19 +480,20 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) addr.sll_ifindex = ifindex; if (bind(iapp->packet_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind[PACKET]"); + wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, iapp, NULL)) { - printf("Could not register read socket for IAPP.\n"); + wpa_printf(MSG_INFO, "Could not register read socket for IAPP"); iapp_deinit(iapp); return NULL; } - printf("IEEE 802.11F (IAPP) using interface %s\n", iface); + wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface); /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually @@ -515,7 +518,8 @@ void iapp_deinit(struct iapp_data *iapp) mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]"); + wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s", + strerror(errno)); } eloop_unregister_read_sock(iapp->udp_sock); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 51c8d286d868..89911b1fdd42 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,10 +13,12 @@ #include "utils/common.h" #include "utils/eloop.h" #include "crypto/crypto.h" -#include "drivers/driver.h" +#include "crypto/sha256.h" +#include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -27,6 +29,7 @@ #include "sta_info.h" #include "ieee802_1x.h" #include "wpa_auth.h" +#include "pmksa_cache_auth.h" #include "wmm.h" #include "ap_list.h" #include "accounting.h" @@ -36,6 +39,7 @@ #include "ap_drv_ops.h" #include "wnm_ap.h" #include "ieee802_11.h" +#include "dfs.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) @@ -59,7 +63,6 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) } *pos++ = num; - count = 0; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; i++) { count++; @@ -102,7 +105,6 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = num; - count = 0; for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; i++) { count++; @@ -135,6 +137,15 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, { int capab = WLAN_CAPABILITY_ESS; int privacy; + int dfs; + + /* Check if any of configured channels require DFS */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) { + wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d", + dfs); + dfs = 0; + } if (hapd->iface->num_sta_no_short_preamble == 0 && hapd->iconf->preamble == SHORT_PREAMBLE) @@ -150,6 +161,11 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->wpa) privacy = 1; +#ifdef CONFIG_HS20 + if (hapd->conf->osen) + privacy = 1; +#endif /* CONFIG_HS20 */ + if (sta) { int policy, def_klen; if (probe && sta->ssid_probe) { @@ -172,22 +188,21 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, hapd->iface->num_sta_no_short_slot_time == 0) capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - return capab; -} + /* + * Currently, Spectrum Management capability bit is set when directly + * requested in configuration by spectrum_mgmt_required or when AP is + * running on DFS channel. + * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit + */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + (hapd->iconf->spectrum_mgmt_required || dfs)) + capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; + if (hapd->conf->radio_measurements) + capab |= IEEE80211_CAP_RRM; -void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) -{ - int i; - if (len > HOSTAPD_MAX_SSID_LEN) - len = HOSTAPD_MAX_SSID_LEN; - for (i = 0; i < len; i++) { - if (ssid[i] >= 32 && ssid[i] < 127) - buf[i] = ssid[i]; - else - buf[i] = '.'; - } - buf[len] = '\0'; + return capab; } @@ -225,7 +240,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, /* Transaction 3 */ if (!iswep || !sta->challenge || !challenge || - os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { + os_memcmp_const(sta->challenge, challenge, + WLAN_AUTH_CHALLENGE_LEN)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "shared key authentication - invalid " @@ -236,13 +252,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (shared key)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); -#endif os_free(sta->challenge); sta->challenge = NULL; @@ -283,7 +294,7 @@ static void send_auth_reply(struct hostapd_data *hapd, MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) - perror("send_auth_reply: send"); + wpa_printf(MSG_INFO, "send_auth_reply: send"); os_free(buf); } @@ -317,19 +328,34 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #ifdef CONFIG_SAE +#define dot11RSNASAERetransPeriod 40 /* msec */ +#define dot11RSNASAESync 5 /* attempts */ + + static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, - struct sta_info *sta) + struct sta_info *sta, int update) { struct wpabuf *buf; - buf = wpabuf_alloc(2); - if (buf == NULL) + if (hapd->conf->ssid.wpa_passphrase == NULL) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; + } + + if (update && + sae_prepare_commit(hapd->own_addr, sta->addr, + (u8 *) hapd->conf->ssid.wpa_passphrase, + os_strlen(hapd->conf->ssid.wpa_passphrase), + sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } - wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */ - /* TODO: Anti-Clogging Token (if requested) */ - /* TODO: Scalar */ - /* TODO: Element */ + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + if (buf == NULL) + return NULL; + sae_write_commit(sta->sae, buf, sta->sae->tmp ? + sta->sae->tmp->anti_clogging_token : NULL); return buf; } @@ -340,114 +366,517 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, { struct wpabuf *buf; - buf = wpabuf_alloc(2); + buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; - wpabuf_put_le16(buf, sta->sae_send_confirm); - sta->sae_send_confirm++; - /* TODO: Confirm */ + sae_write_confirm(sta->sae, buf); return buf; } -static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *data, size_t len) +static int auth_sae_send_commit(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *bssid, int update) { - wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len); + struct wpabuf *data; - /* Check Finite Cyclic Group */ - if (len < 2) + data = auth_build_sae_commit(hapd, sta, update); + if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - if (WPA_GET_LE16(data) != 19) { - wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", - WPA_GET_LE16(data)); - return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; - } + + send_auth_reply(hapd, sta->addr, bssid, + WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, + wpabuf_head(data), wpabuf_len(data)); + + wpabuf_free(data); + + return WLAN_STATUS_SUCCESS; +} + + +static int auth_sae_send_confirm(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *bssid) +{ + struct wpabuf *data; + + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + send_auth_reply(hapd, sta->addr, bssid, + WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, + wpabuf_head(data), wpabuf_len(data)); + + wpabuf_free(data); return WLAN_STATUS_SUCCESS; } -static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *data, size_t len) +static int use_sae_anti_clogging(struct hostapd_data *hapd) +{ + struct sta_info *sta; + unsigned int open = 0; + + if (hapd->conf->sae_anti_clogging_threshold == 0) + return 1; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!sta->sae) + continue; + if (sta->sae->state != SAE_COMMITTED && + sta->sae->state != SAE_CONFIRMED) + continue; + open++; + if (open >= hapd->conf->sae_anti_clogging_threshold) + return 1; + } + + return 0; +} + + +static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, + const u8 *token, size_t token_len) +{ + u8 mac[SHA256_MAC_LEN]; + + if (token_len != SHA256_MAC_LEN) + return -1; + if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, mac) < 0 || + os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0) + return -1; + + return 0; +} + + +static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, + int group, const u8 *addr) +{ + struct wpabuf *buf; + u8 *token; + struct os_reltime now; + + os_get_reltime(&now); + if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || + os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { + if (random_get_bytes(hapd->sae_token_key, + sizeof(hapd->sae_token_key)) < 0) + return NULL; + wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", + hapd->sae_token_key, sizeof(hapd->sae_token_key)); + hapd->last_sae_token_key_update = now; + } + + buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + + token = wpabuf_put(buf, SHA256_MAC_LEN); + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, token); + + return buf; +} + + +static int sae_check_big_sync(struct sta_info *sta) +{ + if (sta->sae->sync > dot11RSNASAESync) { + sta->sae->state = SAE_NOTHING; + sta->sae->sync = 0; + return -1; + } + return 0; +} + + +static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = eloop_data; + int ret; + + if (sae_check_big_sync(sta)) + return; + sta->sae->sync++; + + switch (sta->sae->state) { + case SAE_COMMITTED: + ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); + eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + auth_sae_retransmit_timer, hapd, sta); + break; + case SAE_CONFIRMED: + ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr); + eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + auth_sae_retransmit_timer, hapd, sta); + break; + default: + ret = -1; + break; + } + + if (ret != WLAN_STATUS_SUCCESS) + wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret); +} + + +void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); +} + + +static void sae_set_retransmit_timer(struct hostapd_data *hapd, + struct sta_info *sta) { - u16 rc; + if (!(hapd->conf->mesh & MESH_ENABLED)) + return; + + eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta); + eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000, + auth_sae_retransmit_timer, hapd, sta); +} - wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, len); - if (len < 2) +static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *bssid, u8 auth_transaction) +{ + int ret; + + if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; - rc = WPA_GET_LE16(data); - wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc); + switch (sta->sae->state) { + case SAE_NOTHING: + if (auth_transaction == 1) { + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + sta->sae->state = SAE_COMMITTED; + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* + * In mesh case, both Commit and Confirm can be sent + * immediately. In infrastructure BSS, only a single + * Authentication frame (Commit) is expected from the AP + * here and the second one (Confirm) will be sent once + * the STA has sent its second Authentication frame + * (Confirm). + */ + if (hapd->conf->mesh & MESH_ENABLED) { + /* + * Send both Commit and Confirm immediately + * based on SAE finite state machine + * Nothing -> Confirm transition. + */ + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + sta->sae->state = SAE_CONFIRMED; + } else { + /* + * For infrastructure BSS, send only the Commit + * message now to get alternating sequence of + * Authentication frames between the AP and STA. + * Confirm will be sent in + * Commited -> Confirmed/Accepted transition + * when receiving Confirm from STA. + */ + } + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE confirm before commit"); + } + break; + case SAE_COMMITTED: + sae_clear_retransmit_timer(hapd, sta); + if (auth_transaction == 1) { + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + sta->sae->state = SAE_CONFIRMED; + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + } else if (hapd->conf->mesh & MESH_ENABLED) { + /* + * In mesh case, follow SAE finite state machine and + * send Commit now, if sync count allows. + */ + if (sae_check_big_sync(sta)) + return WLAN_STATUS_SUCCESS; + sta->sae->sync++; + + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + + sae_set_retransmit_timer(hapd, sta); + } else { + /* + * For instructure BSS, send the postponed Confirm from + * Nothing -> Confirmed transition that was reduced to + * Nothing -> Committed above. + */ + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + + sta->sae->state = SAE_CONFIRMED; + + /* + * Since this was triggered on Confirm RX, run another + * step to get to Accepted without waiting for + * additional events. + */ + return sae_sm_step(hapd, sta, bssid, auth_transaction); + } + break; + case SAE_CONFIRMED: + sae_clear_retransmit_timer(hapd, sta); + if (auth_transaction == 1) { + if (sae_check_big_sync(sta)) + return WLAN_STATUS_SUCCESS; + sta->sae->sync++; + + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + + sae_set_retransmit_timer(hapd, sta); + } else { + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->sae->state = SAE_ACCEPTED; + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + sta->sae->pmk); + } + break; + case SAE_ACCEPTED: + if (auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR + ") doing reauthentication", + MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); + } else { + if (sae_check_big_sync(sta)) + return WLAN_STATUS_SUCCESS; + sta->sae->sync++; + + ret = auth_sae_send_confirm(hapd, sta, bssid); + sae_clear_temp_data(sta->sae); + if (ret) + return ret; + } + break; + default: + wpa_printf(MSG_ERROR, "SAE: invalid state %d", + sta->sae->state); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } return WLAN_STATUS_SUCCESS; } static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, - u8 auth_transaction) + u16 auth_transaction, u16 status_code) { u16 resp = WLAN_STATUS_SUCCESS; - struct wpabuf *data; + struct wpabuf *data = NULL; + + if (!sta->sae) { + if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) + return; + sta->sae = os_zalloc(sizeof(*sta->sae)); + if (sta->sae == NULL) + return; + sta->sae->state = SAE_NOTHING; + sta->sae->sync = 0; + } if (auth_transaction == 1) { + const u8 *token = NULL, *pos, *end; + size_t token_len = 0; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "start SAE authentication (RX commit)"); - resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable, - ((u8 *) mgmt) + len - - mgmt->u.auth.variable); - if (resp == WLAN_STATUS_SUCCESS) - sta->sae_state = SAE_COMMIT; - } else if (auth_transaction == 2) { - if (sta->sae_state != SAE_COMMIT) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "SAE confirm before commit"); - resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + "start SAE authentication (RX commit, status=%u)", + status_code); + + if ((hapd->conf->mesh & MESH_ENABLED) && + status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && + sta->sae->tmp) { + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + if (pos + sizeof(le16) > end) { + wpa_printf(MSG_ERROR, + "SAE: Too short anti-clogging token request"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + resp = sae_group_allowed(sta->sae, + hapd->conf->sae_groups, + WPA_GET_LE16(pos)); + if (resp != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_ERROR, + "SAE: Invalid group in anti-clogging token request"); + goto reply; + } + pos += sizeof(le16); + + wpabuf_free(sta->sae->tmp->anti_clogging_token); + sta->sae->tmp->anti_clogging_token = + wpabuf_alloc_copy(pos, end - pos); + if (sta->sae->tmp->anti_clogging_token == NULL) { + wpa_printf(MSG_ERROR, + "SAE: Failed to alloc for anti-clogging token"); + return; + } + + /* + * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code + * is 76, a new Commit Message shall be constructed + * with the Anti-Clogging Token from the received + * Authentication frame, and the commit-scalar and + * COMMIT-ELEMENT previously sent. + */ + if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { + wpa_printf(MSG_ERROR, + "SAE: Failed to send commit message"); + return; + } + sta->sae->state = SAE_COMMITTED; + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + return; } + + if (status_code != WLAN_STATUS_SUCCESS) + return; + + resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, + ((const u8 *) mgmt) + len - + mgmt->u.auth.variable, &token, + &token_len, hapd->conf->sae_groups); + if (token && check_sae_token(hapd, sta->addr, token, token_len) + < 0) { + wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " + "incorrect token from " MACSTR, + MAC2STR(sta->addr)); + return; + } + + if (resp != WLAN_STATUS_SUCCESS) + goto reply; + + if (!token && use_sae_anti_clogging(hapd)) { + wpa_printf(MSG_DEBUG, + "SAE: Request anti-clogging token from " + MACSTR, MAC2STR(sta->addr)); + data = auth_build_token_req(hapd, sta->sae->group, + sta->addr); + resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; + if (hapd->conf->mesh & MESH_ENABLED) + sta->sae->state = SAE_NOTHING; + goto reply; + } + + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); + } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "SAE authentication (RX confirm)"); - resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable, - ((u8 *) mgmt) + len - - mgmt->u.auth.variable); - if (resp == WLAN_STATUS_SUCCESS) { - sta->flags |= WLAN_STA_AUTH; - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->auth_alg = WLAN_AUTH_SAE; - mlme_authenticate_indication(hapd, sta); + "SAE authentication (RX confirm, status=%u)", + status_code); + if (status_code != WLAN_STATUS_SUCCESS) + return; + if (sta->sae->state >= SAE_CONFIRMED || + !(hapd->conf->mesh & MESH_ENABLED)) { + if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } } + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "unexpected SAE authentication transaction %u", - auth_transaction); + "unexpected SAE authentication transaction %u (status=%u)", + auth_transaction, status_code); + if (status_code != WLAN_STATUS_SUCCESS) + return; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } - sta->auth_alg = WLAN_AUTH_SAE; +reply: + if (resp != WLAN_STATUS_SUCCESS) { + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0); + } + wpabuf_free(data); +} - if (resp == WLAN_STATUS_SUCCESS) { - if (auth_transaction == 1) - data = auth_build_sae_commit(hapd, sta); - else - data = auth_build_sae_confirm(hapd, sta); - if (data == NULL) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - } else - data = NULL; - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, - auth_transaction, resp, - data ? wpabuf_head(data) : (u8 *) "", - data ? wpabuf_len(data) : 0); - wpabuf_free(data); +/** + * auth_sae_init_committed - Send COMMIT and start SAE in committed state + * @hapd: BSS data for the device initiating the authentication + * @sta: the peer to which commit authentication frame is sent + * + * This function implements Init event handling (IEEE Std 802.11-2012, + * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the + * sta->sae structure should be initialized appropriately via a call to + * sae_prepare_commit(). + */ +int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) +{ + int ret; + + if (!sta->sae || !sta->sae->tmp) + return -1; + + if (sta->sae->state != SAE_NOTHING) + return -1; + + ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0); + if (ret) + return -1; + + sta->sae->state = SAE_COMMITTED; + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); + + return 0; } + #endif /* CONFIG_SAE */ @@ -467,17 +896,29 @@ static void handle_auth(struct hostapd_data *hapd, size_t resp_ies_len = 0; char *identity = NULL; char *radius_cui = NULL; + u16 seq_ctrl; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", + (unsigned long) len); return; } +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_auth_probability > 0.0 && + drand48() < hapd->iconf->ignore_auth_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring auth frame from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); fc = le_to_host16(mgmt->frame_control); + seq_ctrl = le_to_host16(mgmt->seq_ctrl); if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + WLAN_AUTH_CHALLENGE_LEN && @@ -486,10 +927,12 @@ static void handle_auth(struct hostapd_data *hapd, challenge = &mgmt->u.auth.variable[2]; wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d status_code=%d wep=%d%s", + "auth_transaction=%d status_code=%d wep=%d%s " + "seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), - challenge ? " challenge" : ""); + challenge ? " challenge" : "", + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; @@ -508,23 +951,23 @@ static void handle_auth(struct hostapd_data *hapd, #endif /* CONFIG_SAE */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { - printf("Unsupported authentication algorithm (%d)\n", - auth_alg); + wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", + auth_alg); resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; goto fail; } if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { - printf("Unknown authentication transaction number (%d)\n", - auth_transaction); + wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", + auth_transaction); resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; goto fail; } if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); + wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", + MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -535,8 +978,8 @@ static void handle_auth(struct hostapd_data *hapd, &psk, &identity, &radius_cui); if (res == HOSTAPD_ACL_REJECT) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); + wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", + MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -550,15 +993,49 @@ static void handle_auth(struct hostapd_data *hapd, return; } - sta = ap_sta_add(hapd, mgmt->sa); - if (!sta) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; + sta = ap_get_sta(hapd, mgmt->sa); + if (sta) { + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && + sta->last_subtype == WLAN_FC_STYPE_AUTH) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated authentication frame seq_ctrl=0x%x", + seq_ctrl); + return; + } + } else { +#ifdef CONFIG_MESH + if (hapd->conf->mesh & MESH_ENABLED) { + /* if the mesh peer is not available, we don't do auth. + */ + wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR + " not yet known - drop Authentiation frame", + MAC2STR(mgmt->sa)); + /* + * Save a copy of the frame so that it can be processed + * if a new peer entry is added shortly after this. + */ + wpabuf_free(hapd->mesh_pending_auth); + hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len); + os_get_reltime(&hapd->mesh_pending_auth_time); + return; + } +#endif /* CONFIG_MESH */ + + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } } + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = WLAN_FC_STYPE_AUTH; if (vlan_id > 0) { - if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, - vlan_id) == NULL) { + if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " "%d received from RADIUS server", @@ -599,15 +1076,10 @@ static void handle_auth(struct hostapd_data *hapd, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (open system)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else sta->flags |= WLAN_STA_AUTH; wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); sta->auth_alg = WLAN_AUTH_OPEN; mlme_authenticate_indication(hapd, sta); -#endif break; case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, @@ -627,7 +1099,7 @@ static void handle_auth(struct hostapd_data *hapd, sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, NULL); if (sta->wpa_sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " "state machine"); @@ -644,7 +1116,23 @@ static void handle_auth(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: - handle_auth_sae(hapd, sta, mgmt, len, auth_transaction); +#ifdef CONFIG_MESH + if (status_code == WLAN_STATUS_SUCCESS && + hapd->conf->mesh & MESH_ENABLED) { + if (sta->wpa_sm == NULL) + sta->wpa_sm = + wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, + "SAE: Failed to initialize WPA state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } +#endif /* CONFIG_MESH */ + handle_auth_sae(hapd, sta, mgmt, len, auth_transaction, + status_code); return; #endif /* CONFIG_SAE */ } @@ -700,12 +1188,10 @@ static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta, if (ssid_ie_len != hapd->conf->ssid.ssid_len || os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) { - char ssid_txt[33]; - ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station tried to associate with unknown SSID " - "'%s'", ssid_txt); + "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len)); return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -767,6 +1253,21 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, } +static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ext_capab_ie, size_t ext_capab_ie_len) +{ +#ifdef CONFIG_INTERWORKING + /* check for QoS Map support */ + if (ext_capab_ie_len >= 5) { + if (ext_capab_ie[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + + return WLAN_STATUS_SUCCESS; +} + + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) { @@ -774,6 +1275,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, u16 resp; const u8 *wpa_ie; size_t wpa_ie_len; + const u8 *p2p_dev_addr = NULL; if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -786,6 +1288,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len); if (resp != WLAN_STATUS_SUCCESS) return resp; resp = copy_supp_rates(hapd, sta, &elems); @@ -810,15 +1315,40 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, elems.vht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Station does not support " "mandatory VHT PHY - reject association"); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + return WLAN_STATUS_ASSOC_DENIED_NO_VHT; + } + + if (hapd->conf->vendor_vht && !elems.vht_capabilities) { + resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht, + elems.vendor_vht_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (sta->p2p_ie) + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + } else { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = NULL; + } +#endif /* CONFIG_P2P */ + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { wpa_ie = elems.rsn_ie; wpa_ie_len = elems.rsn_ie_len; @@ -870,7 +1400,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_ie_len += 2; if (sta->wpa_sm == NULL) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); + sta->addr, + p2p_dev_addr); if (sta->wpa_sm == NULL) { wpa_printf(MSG_WARNING, "Failed to initialize WPA " "state machine"); @@ -943,7 +1474,21 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_SAE if (wpa_auth_uses_sae(sta->wpa_sm) && - sta->auth_alg != WLAN_AUTH_SAE) { + sta->auth_alg == WLAN_AUTH_OPEN) { + struct rsn_pmksa_cache_entry *sa; + sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) { + wpa_printf(MSG_DEBUG, + "SAE: No PMKSA cache entry found for " + MACSTR, MAC2STR(sta->addr)); + return WLAN_STATUS_INVALID_PMKID; + } + wpa_printf(MSG_DEBUG, "SAE: " MACSTR + " using PMKSA caching", MAC2STR(sta->addr)); + } else if (wpa_auth_uses_sae(sta->wpa_sm) && + sta->auth_alg != WLAN_AUTH_SAE && + !(sta->auth_alg == WLAN_AUTH_FT && + wpa_auth_uses_ft_sae(sta->wpa_sm))) { wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " "SAE AKM after non-SAE auth_alg %u", MAC2STR(sta->addr), sta->auth_alg); @@ -962,20 +1507,33 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_HS20 + } else if (hapd->conf->osen) { + if (elems.osen == NULL) { + hostapd_logger( + hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "No HS 2.0 OSEN element in association request"); + return WLAN_STATUS_INVALID_IE; + } + + wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association"); + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize WPA " + "state machine"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm, + elems.osen - 2, elems.osen_len + 2) < 0) + return WLAN_STATUS_INVALID_IE; +#endif /* CONFIG_HS20 */ } else wpa_auth_sta_no_wpa(sta->wpa_sm); #ifdef CONFIG_P2P - if (elems.p2p) { - wpabuf_free(sta->p2p_ie); - sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, - P2P_IE_VENDOR_TYPE); - - } else { - wpabuf_free(sta->p2p_ie); - sta->p2p_ie = NULL; - } - p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); #endif /* CONFIG_P2P */ @@ -1038,8 +1596,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); reply->u.assoc_resp.status_code = host_to_le16(status_code); - reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) - | BIT(14) | BIT(15)); + reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ @@ -1066,12 +1623,21 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - p = hostapd_eid_vht_capabilities(hapd, p); - p = hostapd_eid_vht_operation(hapd, p); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); + } #endif /* CONFIG_IEEE80211AC */ p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); + if (sta->qos_map_enabled) + p = hostapd_eid_qos_map_set(hapd, p); + +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + p = hostapd_eid_vendor_vht(hapd, p); +#endif /* CONFIG_IEEE80211AC */ if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); @@ -1130,7 +1696,7 @@ static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { - u16 capab_info, listen_interval; + u16 capab_info, listen_interval, seq_ctrl, fc; u16 resp = WLAN_STATUS_SUCCESS; const u8 *pos; int left, i; @@ -1138,20 +1704,44 @@ static void handle_assoc(struct hostapd_data *hapd, if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { - printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)" - "\n", reassoc, (unsigned long) len); + wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); return; } +#ifdef CONFIG_TESTING_OPTIONS + if (reassoc) { + if (hapd->iconf->ignore_reassoc_probability > 0.0 && + drand48() < hapd->iconf->ignore_reassoc_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring reassoc request from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + } else { + if (hapd->iconf->ignore_assoc_probability > 0.0 && + drand48() < hapd->iconf->ignore_assoc_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring assoc request from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + + fc = le_to_host16(mgmt->frame_control); + seq_ctrl = le_to_host16(mgmt->seq_ctrl); + if (reassoc) { capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.reassoc_req.listen_interval); wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d current_ap=" - MACSTR, + MACSTR " seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), capab_info, listen_interval, - MAC2STR(mgmt->u.reassoc_req.current_ap)); + MAC2STR(mgmt->u.reassoc_req.current_ap), + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); pos = mgmt->u.reassoc_req.variable; } else { @@ -1159,8 +1749,10 @@ static void handle_assoc(struct hostapd_data *hapd, listen_interval = le_to_host16( mgmt->u.assoc_req.listen_interval); wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR - " capab_info=0x%02x listen_interval=%d", - MAC2STR(mgmt->sa), capab_info, listen_interval); + " capab_info=0x%02x listen_interval=%d " + "seq_ctrl=0x%x%s", + MAC2STR(mgmt->sa), capab_info, listen_interval, + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); pos = mgmt->u.assoc_req.variable; } @@ -1186,6 +1778,21 @@ static void handle_assoc(struct hostapd_data *hapd, return; } + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && + sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated association frame seq_ctrl=0x%x", + seq_ctrl); + return; + } + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ; + if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; @@ -1279,17 +1886,6 @@ static void handle_assoc(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211W */ - if (reassoc) { - os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, - ETH_ALEN); - } - - if (sta->last_assoc_req) - os_free(sta->last_assoc_req); - sta->last_assoc_req = os_malloc(len); - if (sta->last_assoc_req) - os_memcpy(sta->last_assoc_req, mgmt, len); - /* Make sure that the previously registered inactivity timer will not * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; @@ -1305,8 +1901,8 @@ static void handle_disassoc(struct hostapd_data *hapd, struct sta_info *sta; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { - printf("handle_disassoc - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)", + (unsigned long) len); return; } @@ -1316,12 +1912,13 @@ static void handle_disassoc(struct hostapd_data *hapd, sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { - printf("Station " MACSTR " trying to disassociate, but it " - "is not associated.\n", MAC2STR(mgmt->sa)); + wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated", + MAC2STR(mgmt->sa)); return; } ap_sta_set_authorized(hapd, sta, 0); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -1332,6 +1929,9 @@ static void handle_disassoc(struct hostapd_data *hapd, * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); if (sta->timeout_next == STA_NULLFUNC || @@ -1371,6 +1971,7 @@ static void handle_deauth(struct hostapd_data *hapd, } ap_sta_set_authorized(hapd, sta, 0); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); @@ -1391,8 +1992,8 @@ static void handle_beacon(struct hostapd_data *hapd, struct ieee802_11_elems elems; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { - printf("handle_beacon - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)", + (unsigned long) len); return; } @@ -1407,9 +2008,9 @@ static void handle_beacon(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W -static void hostapd_sa_query_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, - size_t len) +static int hostapd_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) { const u8 *end; @@ -1418,12 +2019,13 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd, if (((u8 *) mgmt) + len < end) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " "frame (len=%lu)", (unsigned long) len); - return; + return 0; } ieee802_11_sa_query_action(hapd, mgmt->sa, mgmt->u.action.u.sa_query_resp.action, mgmt->u.action.u.sa_query_resp.trans_id); + return 1; } @@ -1435,29 +2037,8 @@ static int robust_action_frame(u8 category) #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM -static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta, - const struct ieee80211_mgmt *mgmt, - size_t len) -{ - struct rx_action action; - if (len < IEEE80211_HDRLEN + 2) - return; - os_memset(&action, 0, sizeof(action)); - action.da = mgmt->da; - action.sa = mgmt->sa; - action.bssid = mgmt->bssid; - action.category = mgmt->u.action.category; - action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action; - action.len = len - IEEE80211_HDRLEN - 1; - action.freq = hapd->iface->freq; - ieee802_11_rx_wnm_action_ap(hapd, &action); -} -#endif /* CONFIG_WNM */ - - -static void handle_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) +static int handle_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; sta = ap_get_sta(hapd, mgmt->sa); @@ -1467,7 +2048,7 @@ static void handle_action(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", (unsigned long) len); - return; + return 0; } if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && @@ -1475,56 +2056,92 @@ static void handle_action(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " "frame (category=%u) from unassociated STA " MACSTR, MAC2STR(mgmt->sa), mgmt->u.action.category); - return; + return 0; } #ifdef CONFIG_IEEE80211W if (sta && (sta->flags & WLAN_STA_MFP) && - !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && - robust_action_frame(mgmt->u.action.category))) { + !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) && + robust_action_frame(mgmt->u.action.category)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Dropped unprotected Robust Action frame from " "an MFP STA"); - return; + return 0; } #endif /* CONFIG_IEEE80211W */ + if (sta) { + u16 fc = le_to_host16(mgmt->frame_control); + u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl); + + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && + sta->last_subtype == WLAN_FC_STYPE_ACTION) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated action frame seq_ctrl=0x%x", + seq_ctrl); + return 1; + } + + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = WLAN_FC_STYPE_ACTION; + } + switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: - if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, + if (!sta || + wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; - return; + return 1; #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); - return; + return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: - hostapd_sa_query_action(hapd, mgmt, len); - return; + return hostapd_sa_query_action(hapd, mgmt, len); #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM case WLAN_ACTION_WNM: - hostapd_wnm_action(hapd, sta, mgmt, len); - return; + ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); + return 1; #endif /* CONFIG_WNM */ case WLAN_ACTION_PUBLIC: + case WLAN_ACTION_PROTECTED_DUAL: +#ifdef CONFIG_IEEE80211N + if (mgmt->u.action.u.public_action.action == + WLAN_PA_20_40_BSS_COEX) { + wpa_printf(MSG_DEBUG, + "HT20/40 coex mgmt frame received from STA " + MACSTR, MAC2STR(mgmt->sa)); + hostapd_2040_coex_action(hapd, mgmt, len); + } +#endif /* CONFIG_IEEE80211N */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq); - return; } + if (hapd->public_action_cb2) { + hapd->public_action_cb2(hapd->public_action_cb2_ctx, + (u8 *) mgmt, len, + hapd->iface->freq); + } + if (hapd->public_action_cb || hapd->public_action_cb2) + return 1; break; case WLAN_ACTION_VENDOR_SPECIFIC: if (hapd->vendor_action_cb) { if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, (u8 *) mgmt, len, hapd->iface->freq) == 0) - return; + return 1; } break; } @@ -1547,7 +2164,7 @@ static void handle_action(struct hostapd_data *hapd, "frame back to sender"); resp = os_malloc(len); if (resp == NULL) - return; + return 0; os_memcpy(resp, mgmt, len); os_memcpy(resp->da, resp->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); @@ -1560,6 +2177,8 @@ static void handle_action(struct hostapd_data *hapd, } os_free(resp); } + + return 1; } @@ -1576,15 +2195,16 @@ static void handle_action(struct hostapd_data *hapd, * addition, it can be called to re-inserted pending frames (e.g., when using * external RADIUS server as an MAC ACL). */ -void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, - struct hostapd_frame_info *fi) +int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; int broadcast; u16 fc, stype; + int ret = 0; if (len < 24) - return; + return 0; mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); @@ -1592,7 +2212,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, if (stype == WLAN_FC_STYPE_BEACON) { handle_beacon(hapd, mgmt, len, fi); - return; + return 1; } broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && @@ -1605,16 +2225,19 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, !((hapd->conf->p2p & P2P_GROUP_OWNER) && stype == WLAN_FC_STYPE_ACTION) && #endif /* CONFIG_P2P */ +#ifdef CONFIG_MESH + !(hapd->conf->mesh & MESH_ENABLED) && +#endif /* CONFIG_MESH */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { - printf("MGMT: BSSID=" MACSTR " not our address\n", - MAC2STR(mgmt->bssid)); - return; + wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", + MAC2STR(mgmt->bssid)); + return 0; } if (stype == WLAN_FC_STYPE_PROBE_REQ) { handle_probe_req(hapd, mgmt, len, fi->ssi_signal); - return; + return 1; } if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { @@ -1622,33 +2245,38 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", MAC2STR(mgmt->da)); - return; + return 0; } switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); handle_auth(hapd, mgmt, len); + ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); handle_assoc(hapd, mgmt, len, 0); + ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); handle_assoc(hapd, mgmt, len, 1); + ret = 1; break; case WLAN_FC_STYPE_DISASSOC: wpa_printf(MSG_DEBUG, "mgmt::disassoc"); handle_disassoc(hapd, mgmt, len); + ret = 1; break; case WLAN_FC_STYPE_DEAUTH: wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); handle_deauth(hapd, mgmt, len); + ret = 1; break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); - handle_action(hapd, mgmt, len); + ret = handle_action(hapd, mgmt, len); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -1656,6 +2284,8 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, "unknown mgmt frame subtype %d", stype); break; } + + return ret; } @@ -1674,8 +2304,8 @@ static void handle_auth_cb(struct hostapd_data *hapd, } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth_cb - too short payload (len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (unsigned long) len); return; } @@ -1685,8 +2315,8 @@ static void handle_auth_cb(struct hostapd_data *hapd, sta = ap_get_sta(hapd, mgmt->da); if (!sta) { - printf("handle_auth_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); + wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); return; } @@ -1700,6 +2330,30 @@ static void handle_auth_cb(struct hostapd_data *hapd, } +static void hostapd_set_wds_encryption(struct hostapd_data *hapd, + struct sta_info *sta, + char *ifname_wds) +{ + int i; + struct hostapd_ssid *ssid = sta->ssid; + + if (hapd->conf->ieee802_1x || hapd->conf->wpa) + return; + + for (i = 0; i < 4; i++) { + if (ssid->wep.key[i] && + hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, + i == ssid->wep.idx, NULL, 0, + ssid->wep.key[i], ssid->wep.len[i])) { + wpa_printf(MSG_WARNING, + "Could not set WEP keys for WDS interface; %s", + ifname_wds); + break; + } + } +} + + static void handle_assoc_cb(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) @@ -1708,18 +2362,19 @@ static void handle_assoc_cb(struct hostapd_data *hapd, struct sta_info *sta; int new_assoc = 1; struct ieee80211_ht_capabilities ht_cap; + struct ieee80211_vht_capabilities vht_cap; if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : sizeof(mgmt->u.assoc_resp))) { - printf("handle_assoc_cb(reassoc=%d) - too short payload " - "(len=%lu)\n", reassoc, (unsigned long) len); + wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); return; } sta = ap_get_sta(hapd, mgmt->da); if (!sta) { - printf("handle_assoc_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); + wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); return; } @@ -1737,7 +2392,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, status = le_to_host16(mgmt->u.assoc_resp.status_code); if (status != WLAN_STATUS_SUCCESS) - goto fail; + return; /* Stop previous accounting session, if one is started, and allocate * new session id for the new session. */ @@ -1751,7 +2406,8 @@ static void handle_assoc_cb(struct hostapd_data *hapd, if (sta->flags & WLAN_STA_ASSOC) new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; - if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) || sta->auth_alg == WLAN_AUTH_FT) { /* * Open, static WEP, or FT protocol; no separate authorization @@ -1780,12 +2436,17 @@ static void handle_assoc_cb(struct hostapd_data *hapd, if (sta->flags & WLAN_STA_HT) hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (sta->flags & WLAN_STA_VHT) + hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +#endif /* CONFIG_IEEE80211AC */ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, sta->supported_rates_len, sta->listen_interval, sta->flags & WLAN_STA_HT ? &ht_cap : NULL, - sta->flags, sta->qosinfo)) { + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags, sta->qosinfo, sta->vht_opmode)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); @@ -1793,11 +2454,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd, ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_DISASSOC_AP_BUSY); - goto fail; + return; } - if (sta->flags & WLAN_STA_WDS) - hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); + if (sta->flags & WLAN_STA_WDS) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + + ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, + sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, ifname_wds); + } if (sta->eapol_sm == NULL) { /* @@ -1806,11 +2474,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd, * interface selection is not going to change anymore. */ if (ap_sta_bind_vlan(hapd, sta, 0) < 0) - goto fail; + return; } else if (sta->vlan_id) { /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ if (ap_sta_bind_vlan(hapd, sta, 0) < 0) - goto fail; + return; } hostapd_set_sta_flags(hapd, sta); @@ -1822,13 +2490,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); - - fail: - /* Copy of the association request is not needed anymore */ - if (sta->last_assoc_req) { - os_free(sta->last_assoc_req); - sta->last_assoc_req = NULL; - } } @@ -1895,6 +2556,14 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, const struct ieee80211_mgmt *mgmt; mgmt = (const struct ieee80211_mgmt *) buf; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_mgmt_frame_handling) { + wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", + stype, ok); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth cb"); @@ -1923,7 +2592,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, wpa_printf(MSG_DEBUG, "mgmt::action cb"); break; default: - printf("unknown mgmt cb frame subtype %d\n", stype); + wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); break; } } @@ -2038,11 +2707,18 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, return; if (wds && !(sta->flags & WLAN_STA_WDS)) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " "STA " MACSTR " (aid %u)", MAC2STR(sta->addr), sta->aid); sta->flags |= WLAN_STA_WDS; - hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1); + ret = hostapd_set_wds_sta(hapd, ifname_wds, + sta->addr, sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, + ifname_wds); } return; } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 1e5800d09c76..41c27d906e72 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -14,12 +14,14 @@ struct hostapd_data; struct sta_info; struct hostapd_frame_info; struct ieee80211_ht_capabilities; +struct ieee80211_mgmt; -void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, - struct hostapd_frame_info *fi); +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 ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); +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, @@ -40,24 +42,37 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, int probe); +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, size_t ht_capab_len); +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, size_t vht_capab_len); +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, @@ -78,4 +93,15 @@ 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/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index c311e55949e3..56c3ce0313d4 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -29,7 +29,7 @@ struct hostapd_cached_radius_acl { - os_time_t timestamp; + struct os_reltime timestamp; macaddr addr; int accepted; /* HOSTAPD_ACL_* */ struct hostapd_cached_radius_acl *next; @@ -43,7 +43,7 @@ struct hostapd_cached_radius_acl { struct hostapd_acl_query_data { - os_time_t timestamp; + struct os_reltime timestamp; u8 radius_id; macaddr addr; u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ @@ -104,15 +104,16 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); for (entry = hapd->acl_cache; entry; entry = entry->next) { if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) continue; - if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT) + if (os_reltime_expired(&now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) return -1; /* entry has expired */ if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) if (session_timeout) @@ -265,7 +266,6 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_REJECT; #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; - struct os_time t; /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, @@ -305,8 +305,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } - os_get_time(&t); - query->timestamp = t.sec; + os_get_reltime(&query->timestamp); os_memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { wpa_printf(MSG_DEBUG, "Failed to send Access-Request " @@ -338,7 +337,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, #ifndef CONFIG_NO_RADIUS -static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, + struct os_reltime *now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; @@ -346,7 +346,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) entry = hapd->acl_cache; while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + if (os_reltime_expired(now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) @@ -367,7 +368,7 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) static void hostapd_acl_expire_queries(struct hostapd_data *hapd, - os_time_t now) + struct os_reltime *now) { struct hostapd_acl_query_data *prev, *entry, *tmp; @@ -375,7 +376,8 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, entry = hapd->acl_queries; while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + if (os_reltime_expired(now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "ACL query for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) @@ -403,11 +405,11 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); - hostapd_acl_expire_cache(hapd, now.sec); - hostapd_acl_expire_queries(hapd, now.sec); + os_get_reltime(&now); + hostapd_acl_expire_cache(hapd, &now); + hostapd_acl_expire_queries(hapd, &now); eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } @@ -480,7 +482,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - struct os_time t; query = hapd->acl_queries; prev = NULL; @@ -515,8 +516,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } - os_get_time(&t); - cache->timestamp = t.sec; + os_get_reltime(&cache->timestamp); os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { u8 *buf; diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 6c3696f5e369..4b0653de95db 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -3,26 +3,22 @@ * Copyright (c) 2002-2009, Jouni Malinen * Copyright (c) 2007-2008, Intel Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This 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 "hostapd.h" #include "ap_config.h" #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" +#include "hw_features.h" +#include "ap_drv_ops.h" u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) @@ -50,6 +46,35 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) pos += sizeof(*cap); + if (hapd->iconf->obss_interval) { + struct ieee80211_obss_scan_parameters *scan_params; + + *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; + *pos++ = sizeof(*scan_params); + + scan_params = (struct ieee80211_obss_scan_parameters *) pos; + os_memset(scan_params, 0, sizeof(*scan_params)); + scan_params->width_trigger_scan_interval = + host_to_le16(hapd->iconf->obss_interval); + + /* Fill in default values for remaining parameters + * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */ + scan_params->scan_passive_dwell = + host_to_le16(20); + scan_params->scan_active_dwell = + host_to_le16(10); + scan_params->scan_passive_total_per_channel = + host_to_le16(200); + scan_params->scan_active_total_per_channel = + host_to_le16(20); + scan_params->channel_transition_delay_factor = + host_to_le16(5); + scan_params->scan_activity_threshold = + host_to_le16(25); + + pos += sizeof(*scan_params); + } + return pos; } @@ -68,14 +93,14 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) oper = (struct ieee80211_ht_operation *) pos; os_memset(oper, 0, sizeof(*oper)); - oper->control_chan = hapd->iconf->channel; + oper->primary_chan = hapd->iconf->channel; oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); if (hapd->iconf->secondary_channel == 1) oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | - HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + HT_INFO_HT_PARAM_STA_CHNL_WIDTH; if (hapd->iconf->secondary_channel == -1) oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | - HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + HT_INFO_HT_PARAM_STA_CHNL_WIDTH; pos += sizeof(*oper); @@ -105,45 +130,40 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", __func__, iface->ht_op_mode); - if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) + if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) && iface->num_sta_ht_no_gf) { - iface->ht_op_mode |= - HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; op_mode_changes++; } else if ((iface->ht_op_mode & - HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && + HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) && iface->num_sta_ht_no_gf == 0) { - iface->ht_op_mode &= - ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; op_mode_changes++; } - if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && (iface->num_sta_no_ht || iface->olbc_ht)) { - iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; op_mode_changes++; } else if ((iface->ht_op_mode & - HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { - iface->ht_op_mode &= - ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; op_mode_changes++; } - new_op_mode = 0; if (iface->num_sta_no_ht) - new_op_mode = OP_MODE_MIXED; - else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) - && iface->num_sta_ht_20mhz) - new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; + new_op_mode = HT_PROT_NON_HT_MIXED; + else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz) + new_op_mode = HT_PROT_20MHZ_PROTECTION; else if (iface->olbc_ht) - new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; + new_op_mode = HT_PROT_NONMEMBER_PROTECTION; else - new_op_mode = OP_MODE_PURE; + new_op_mode = HT_PROT_NO_PROTECTION; - cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; + cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK; if (cur_op_mode != new_op_mode) { - iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; + iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK; iface->ht_op_mode |= new_op_mode; op_mode_changes++; } @@ -155,6 +175,140 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface) } +static int is_40_allowed(struct hostapd_iface *iface, int channel) +{ + int pri_freq, sec_freq; + int affected_start, affected_end; + int pri = 2407 + 5 * channel; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return 1; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + + affected_start = (pri_freq + sec_freq) / 2 - 25; + affected_end = (pri_freq + sec_freq) / 2 + 25; + if ((pri < affected_start || pri > affected_end)) + return 1; /* not within affected channel range */ + + wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri); + return 0; +} + + +void hostapd_2040_coex_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct hostapd_iface *iface = hapd->iface; + struct ieee80211_2040_bss_coex_ie *bc_ie; + struct ieee80211_2040_intol_chan_report *ic_report; + int is_ht_allowed = 1; + int i; + const u8 *start = (const u8 *) mgmt; + const u8 *data = start + IEEE80211_HDRLEN + 2; + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", + mgmt->u.action.u.public_action.action); + + if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; + + if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) + return; + + bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; + if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || + bc_ie->length < 1) { + wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report", + bc_ie->element_id, bc_ie->length); + return; + } + if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) + return; + data += 2 + bc_ie->length; + + wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", + bc_ie->coex_param); + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "20 MHz BSS width request bit is set in BSS coexistence information field"); + is_ht_allowed = 0; + } + + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "40 MHz intolerant bit is set in BSS coexistence information field"); + is_ht_allowed = 0; + } + + if (start + len - data >= 3 && + data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { + u8 ielen = data[1]; + + if (ielen > start + len - data - 2) + return; + ic_report = (struct ieee80211_2040_intol_chan_report *) data; + wpa_printf(MSG_DEBUG, + "20/40 BSS Intolerant Channel Report: Operating Class %u", + ic_report->op_class); + + /* Go through the channel report to find any BSS there in the + * affected channel range */ + for (i = 0; i < ielen - 1; i++) { + u8 chan = ic_report->variable[i]; + + if (is_40_allowed(iface, chan)) + continue; + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "20_40_INTOLERANT channel %d reported", + chan); + is_ht_allowed = 0; + } + } + wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d", + is_ht_allowed, iface->num_sta_ht40_intolerant); + + if (!is_ht_allowed && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + if (iface->conf->secondary_channel) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Switching to 20 MHz operation"); + iface->conf->secondary_channel = 0; + ieee802_11_set_beacons(iface); + } + if (!iface->num_sta_ht40_intolerant && + iface->conf->obss_interval) { + unsigned int delay_time; + delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * + iface->conf->obss_interval; + eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface, + NULL); + eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, + hapd->iface, NULL); + wpa_printf(MSG_DEBUG, + "Reschedule HT 20/40 timeout to occur in %u seconds", + delay_time); + } + } +} + + u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len) { @@ -183,6 +337,52 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, } +void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta) +{ + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return; + + wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR + " in Association Request", MAC2STR(sta->addr)); + + if (sta->ht40_intolerant_set) + return; + + sta->ht40_intolerant_set = 1; + iface->num_sta_ht40_intolerant++; + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); + + if (iface->conf->secondary_channel && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + iface->conf->secondary_channel = 0; + ieee802_11_set_beacons(iface); + } +} + + +void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) +{ + if (!sta->ht40_intolerant_set) + return; + + sta->ht40_intolerant_set = 0; + iface->num_sta_ht40_intolerant--; + + if (iface->num_sta_ht40_intolerant == 0 && + (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { + unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * + iface->conf->obss_interval; + wpa_printf(MSG_DEBUG, + "HT: Start 20->40 MHz transition timer (%d seconds)", + delay_time); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); + eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, + iface, NULL); + } +} + + static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) { u16 ht_capab; @@ -210,6 +410,9 @@ static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) __func__, MAC2STR(sta->addr), hapd->iface->num_sta_ht_20mhz); } + + if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT) + ht40_intolerant_add(hapd->iface, sta); } @@ -271,3 +474,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, neg_ht_cap->ht_capabilities_info = host_to_le16(cap); } + + +void ap_ht2040_timeout(void *eloop_data, void *user_data) +{ + struct hostapd_iface *iface = eloop_data; + + wpa_printf(MSG_INFO, "Switching to 40 MHz operation"); + + iface->conf->secondary_channel = iface->secondary_ch; + ieee802_11_set_beacons(iface); +} diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 76f78a7dbd9c..d462ac8bf9cd 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -24,13 +24,13 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, { u8 *pos = eid; u32 timeout, tu; - struct os_time now, passed; + struct os_reltime now, passed; *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); + 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; @@ -69,7 +69,7 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, 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) - perror("ieee802_11_send_sa_query_req: send"); + wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); } @@ -107,7 +107,7 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, 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) - perror("ieee80211_mgmt_sa_query_request: send"); + wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); } @@ -164,10 +164,62 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, #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; + u8 len = 0, i; if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) len = 5; @@ -175,59 +227,57 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) 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; - *pos++ = 0x00; - *pos++ = 0x00; + for (i = 0; i < len; i++, pos++) { + hostapd_ext_capab_byte(hapd, pos, i); - *pos = 0x00; - if (hapd->conf->wnm_sleep_mode) - *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ - if (hapd->conf->bss_transition) - *pos |= 0x08; /* Bit 19 - BSS Transition */ - pos++; + if (i < hapd->iface->extended_capa_len) { + *pos &= ~hapd->iface->extended_capa_mask[i]; + *pos |= hapd->iface->extended_capa[i]; + } + } - if (len < 4) - return pos; - *pos = 0x00; -#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 */ - pos++; + while (len > 0 && eid[1 + len] == 0) { + len--; + eid[1] = len; + } + if (len == 0) + return eid; - if (len < 5) - return pos; - *pos = 0x00; - if (hapd->conf->tdls & TDLS_PROHIBIT) - *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ - if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) - *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */ - pos++; + return eid + 2 + len; +} - if (len < 6) - return pos; - *pos = 0x00; - pos++; - if (len < 7) - return pos; - *pos = 0x00; - if (hapd->conf->ssid.utf8_ssid) - *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ - pos++; +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; } diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index b21c2b7fb0d2..171538ad74a7 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -12,7 +12,6 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" -#include "drivers/driver.h" #include "hostapd.h" #include "ap_config.h" #include "sta_info.h" @@ -23,23 +22,35 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_vht_capabilities *cap; + struct hostapd_hw_modes *mode = hapd->iface->current_mode; u8 *pos = eid; - if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || - hapd->conf->disable_11ac) + if (!mode) return eid; + if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht && + mode->vht_capab == 0 && hapd->iface->hw_features) { + int i; + + for (i = 0; i < hapd->iface->num_hw_features; i++) { + if (hapd->iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) { + mode = &hapd->iface->hw_features[i]; + break; + } + } + } + *pos++ = WLAN_EID_VHT_CAP; *pos++ = sizeof(*cap); cap = (struct ieee80211_vht_capabilities *) pos; os_memset(cap, 0, sizeof(*cap)); cap->vht_capabilities_info = host_to_le32( - hapd->iface->current_mode->vht_capab); + hapd->iface->conf->vht_capab); /* Supported MCS set comes from hw */ - os_memcpy(cap->vht_supported_mcs_set, - hapd->iface->current_mode->vht_mcs_set, 8); + os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); pos += sizeof(*cap); @@ -52,9 +63,6 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) struct ieee80211_vht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) - return eid; - *pos++ = WLAN_EID_VHT_OPERATION; *pos++ = sizeof(*oper); @@ -82,13 +90,55 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) } +static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, + const u8 *sta_vht_capab) +{ + const struct ieee80211_vht_capabilities *vht_cap; + struct ieee80211_vht_capabilities ap_vht_cap; + u16 sta_rx_mcs_set, ap_tx_mcs_set; + int i; + + if (!mode) + return 1; + + /* + * Disable VHT caps for STAs for which there is not even a single + * allowed MCS in any supported number of streams, i.e., STA is + * advertising 3 (not supported) as VHT MCS rates for all supported + * stream cases. + */ + os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set, + sizeof(ap_vht_cap.vht_supported_mcs_set)); + vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab; + + /* AP Tx MCS map vs. STA Rx MCS map */ + sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map); + ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map); + + for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) { + if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3) + continue; + + if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3) + continue; + + return 1; + } + + wpa_printf(MSG_DEBUG, + "No matching VHT MCS found between AP TX and STA RX"); + return 0; +} + + u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab, size_t vht_capab_len) { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || - hapd->conf->disable_11ac) { + hapd->conf->disable_11ac || + !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; os_free(sta->vht_capabilities); sta->vht_capabilities = NULL; @@ -108,3 +158,140 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; } + + +u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len) +{ + const u8 *vht_capab; + unsigned int vht_capab_len; + + if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) || + hapd->conf->disable_11ac) + goto no_capab; + + /* The VHT Capabilities element embedded in vendor VHT */ + vht_capab = ie + 5; + if (vht_capab[0] != WLAN_EID_VHT_CAP) + goto no_capab; + vht_capab_len = vht_capab[1]; + if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || + (int) vht_capab_len > ie + len - vht_capab - 2) + goto no_capab; + vht_capab += 2; + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + return WLAN_STATUS_SUCCESS; + +no_capab: + sta->flags &= ~WLAN_STA_VENDOR_VHT; + return WLAN_STATUS_SUCCESS; +} + + +u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = (5 + /* The Vendor OUI, type and subtype */ + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation)); + + WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); + pos += 4; + *pos++ = VENDOR_VHT_SUBTYPE; + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); + + return pos; +} + + +u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper_notif) +{ + if (!vht_oper_notif) { + sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED; + return WLAN_STATUS_SUCCESS; + } + + sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED; + sta->vht_opmode = *vht_oper_notif; + return WLAN_STATUS_SUCCESS; +} + + +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap) +{ + u32 cap, own_cap, sym_caps; + + if (vht_cap == NULL) + return; + os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap)); + + cap = le_to_host32(neg_vht_cap->vht_capabilities_info); + own_cap = hapd->iconf->vht_capab; + + /* mask out symmetric VHT capabilities we don't support */ + sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160; + cap &= ~sym_caps | (own_cap & sym_caps); + + /* mask out beamformer/beamformee caps if not supported */ + if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE | + VHT_CAP_BEAMFORMEE_STS_MAX); + + if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE | + VHT_CAP_SOUNDING_DIMENSION_MAX); + + if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE)) + cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE; + + if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE)) + cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE; + + /* mask channel widths we don't support */ + switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + break; + case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { + cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + break; + default: + cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK; + break; + } + + if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK)) + cap &= ~VHT_CAP_SHORT_GI_160; + + /* + * if we don't support RX STBC, mask out TX STBC in the STA's HT caps + * if we don't support TX STBC, mask out RX STBC in the STA's HT caps + */ + if (!(own_cap & VHT_CAP_RXSTBC_MASK)) + cap &= ~VHT_CAP_TXSTBC; + if (!(own_cap & VHT_CAP_TXSTBC)) + cap &= ~VHT_CAP_RXSTBC_MASK; + + neg_vht_cap->vht_capabilities_info = host_to_le32(cap); +} diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index d9c21a8b8117..79dc0f957488 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -29,11 +29,14 @@ #include "pmksa_cache_auth.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wps_hostapd.h" +#include "hs20.h" #include "ieee802_1x.h" static void ieee802_1x_finished(struct hostapd_data *hapd, - struct sta_info *sta, int success); + struct sta_info *sta, int success, + int remediation); static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, @@ -63,6 +66,20 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io) { + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "EAPOL-TX " MACSTR " %s", + MAC2STR(sta->addr), hex); + os_free(hex); + } + } else +#endif /* CONFIG_TESTING_OPTIONS */ if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { @@ -96,12 +113,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, } if (res && errno != ENOENT) { - printf("Could not set station " MACSTR " flags for kernel " - "driver (errno=%d).\n", MAC2STR(sta->addr), errno); + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR + " flags for kernel driver (errno=%d).", + MAC2STR(sta->addr), errno); } if (authorized) { - os_get_time(&sta->connected_time); + os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); } } @@ -186,114 +204,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } -#ifndef CONFIG_NO_VLAN -static struct hostapd_wep_keys * -ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) -{ - struct hostapd_wep_keys *key; - - key = os_zalloc(sizeof(*key)); - if (key == NULL) - return NULL; - - key->default_len = hapd->conf->default_wep_key_len; - - if (key->idx >= hapd->conf->broadcast_key_idx_max || - key->idx < hapd->conf->broadcast_key_idx_min) - key->idx = hapd->conf->broadcast_key_idx_min; - else - key->idx++; - - if (!key->key[key->idx]) - key->key[key->idx] = os_malloc(key->default_len); - if (key->key[key->idx] == NULL || - random_get_bytes(key->key[key->idx], key->default_len)) { - printf("Could not generate random WEP key (dynamic VLAN).\n"); - os_free(key->key[key->idx]); - key->key[key->idx] = NULL; - os_free(key); - return NULL; - } - key->len[key->idx] = key->default_len; - - wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n", - ifname, key->idx); - wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", - key->key[key->idx], key->len[key->idx]); - - if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP, - broadcast_ether_addr, key->idx, 1, - NULL, 0, key->key[key->idx], - key->len[key->idx])) - printf("Could not set dynamic VLAN WEP encryption key.\n"); - - hostapd_set_drv_ieee8021x(hapd, ifname, 1); - - return key; -} - - -static struct hostapd_wep_keys * -ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, - size_t vlan_id) -{ - const char *ifname; - - if (vlan_id == 0) - return &ssid->wep; - - if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && - ssid->dyn_vlan_keys[vlan_id]) - return ssid->dyn_vlan_keys[vlan_id]; - - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group " - "state machine for VLAN ID %lu", - (unsigned long) vlan_id); - - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); - if (ifname == NULL) { - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - " - "cannot create group key state machine", - (unsigned long) vlan_id); - return NULL; - } - - if (ssid->dyn_vlan_keys == NULL) { - int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - ssid->dyn_vlan_keys = os_zalloc(size); - if (ssid->dyn_vlan_keys == NULL) - return NULL; - ssid->max_dyn_vlan_keys = vlan_id; - } - - if (ssid->max_dyn_vlan_keys < vlan_id) { - struct hostapd_wep_keys **na; - int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - na = os_realloc(ssid->dyn_vlan_keys, size); - if (na == NULL) - return NULL; - ssid->dyn_vlan_keys = na; - os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, - (vlan_id - ssid->max_dyn_vlan_keys) * - sizeof(ssid->dyn_vlan_keys[0])); - ssid->max_dyn_vlan_keys = vlan_id; - } - - ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); - - return ssid->dyn_vlan_keys[vlan_id]; -} -#endif /* CONFIG_NO_VLAN */ - - void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; -#ifndef CONFIG_NO_VLAN - struct hostapd_wep_keys *key = NULL; - int vlan_id; -#endif /* CONFIG_NO_VLAN */ if (sm == NULL || !sm->eap_if->eapKeyData) return; @@ -302,18 +216,12 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) MAC2STR(sta->addr)); #ifndef CONFIG_NO_VLAN - vlan_id = sta->vlan_id; - if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) - vlan_id = 0; - - if (vlan_id) { - key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); - if (key && key->key[key->idx]) - ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, - key->key[key->idx], - key->len[key->idx]); - } else + if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); + return; + } #endif /* CONFIG_NO_VLAN */ + if (eapol->default_wep_key) { ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1, eapol->default_wep_key, @@ -388,9 +296,15 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, { const u8 *identity; size_t identity_len; + const struct eap_hdr *hdr = (const struct eap_hdr *) eap; if (len <= sizeof(struct eap_hdr) || - eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + (hdr->code == EAP_CODE_RESPONSE && + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) || + (hdr->code == EAP_CODE_INITIATE && + eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) || + (hdr->code != EAP_CODE_RESPONSE && + hdr->code != EAP_CODE_INITIATE)) return; identity = eap_get_identity(sm->eap, &identity_len); @@ -399,21 +313,80 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, /* Save station identity for future RADIUS packets */ os_free(sm->identity); - sm->identity = os_malloc(identity_len + 1); + sm->identity = (u8 *) dup_binstr(identity, identity_len); if (sm->identity == NULL) { sm->identity_len = 0; return; } - os_memcpy(sm->identity, identity, identity_len); sm->identity_len = identity_len; - sm->identity[identity_len] = '\0'; hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); sm->dot1xAuthEapolRespIdFramesRx++; } +static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + u32 suite; + int ver, val; + + ver = wpa_auth_sta_wpa_version(sta->wpa_sm); + val = wpa_auth_get_pairwise(sta->wpa_sm); + suite = wpa_cipher_to_suite(ver, val); + if (val != -1 && + !hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, + suite)) { + wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher"); + return -1; + } + + suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + hapd->conf->wpa_group); + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_GROUP_CIPHER) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER, + suite)) { + wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher"); + return -1; + } + + val = wpa_auth_sta_key_mgmt(sta->wpa_sm); + suite = wpa_akm_to_suite(val); + if (val != -1 && + !hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_AKM_SUITE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE, + suite)) { + wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite"); + return -1; + } + +#ifdef CONFIG_IEEE80211W + if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, + hapd->conf->group_mgmt_cipher); + if (!hostapd_config_get_radius_attr( + req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) && + !radius_msg_add_attr_int32( + msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) { + wpa_printf(MSG_ERROR, + "Could not add WLAN-Group-Mgmt-Cipher"); + return -1; + } + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + static int add_common_radius_sta_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, @@ -465,6 +438,25 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } } +#ifdef CONFIG_IEEE80211R + if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && + sta->wpa_sm && + (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || + sta->auth_alg == WLAN_AUTH_FT) && + !hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_MOBILITY_DOMAIN_ID) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID, + WPA_GET_BE16( + hapd->conf->mobility_domain))) { + wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); + return -1; + } +#endif /* CONFIG_IEEE80211R */ + + if (hapd->conf->wpa && sta->wpa_sm && + add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) + return -1; + return 0; } @@ -528,6 +520,22 @@ int add_common_radius_attr(struct hostapd_data *hapd, return -1; } +#ifdef CONFIG_INTERWORKING + if (hapd->conf->interworking && + !is_zero_ether_addr(hapd->conf->hessid)) { + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(hapd->conf->hessid)); + buf[sizeof(buf) - 1] = '\0'; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_HESSID) && + !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID"); + return -1; + } + } +#endif /* CONFIG_INTERWORKING */ + if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) return -1; @@ -564,7 +572,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, sm->radius_identifier); if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); return; } @@ -573,7 +581,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, if (sm->identity && !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, sm->identity, sm->identity_len)) { - printf("Could not add User-Name\n"); + wpa_printf(MSG_INFO, "Could not add User-Name"); goto fail; } @@ -587,12 +595,12 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, RADIUS_ATTR_FRAMED_MTU) && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { - printf("Could not add Framed-MTU\n"); + wpa_printf(MSG_INFO, "Could not add Framed-MTU"); goto fail; } if (eap && !radius_msg_add_eap(msg, eap, len)) { - printf("Could not add EAP-Message\n"); + wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } @@ -604,8 +612,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, int res = radius_msg_copy_attr(msg, sm->last_recv_radius, RADIUS_ATTR_STATE); if (res < 0) { - printf("Could not copy State attribute from previous " - "Access-Challenge\n"); + wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge"); goto fail; } if (res > 0) { @@ -632,6 +639,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } +#ifdef CONFIG_HS20 + if (hapd->conf->hs20) { + u8 ver = 1; /* Release 2 */ + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, + &ver, 1)) { + wpa_printf(MSG_ERROR, "Could not add HS 2.0 AP " + "version"); + goto fail; + } + + if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) { + const u8 *pos; + u8 buf[3]; + u16 id; + pos = wpabuf_head_u8(sta->hs20_ie); + buf[0] = (*pos) >> 4; + if (((*pos) & HS20_PPS_MO_ID_PRESENT) && + wpabuf_len(sta->hs20_ie) >= 3) + id = WPA_GET_LE16(pos + 1); + else + id = 0; + WPA_PUT_BE16(buf + 1, id); + if (!radius_msg_add_wfa( + msg, + RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION, + buf, sizeof(buf))) { + wpa_printf(MSG_ERROR, "Could not add HS 2.0 " + "STA version"); + goto fail; + } + } + } +#endif /* CONFIG_HS20 */ + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) goto fail; @@ -655,7 +697,7 @@ static void handle_eap_response(struct hostapd_data *hapd, data = (u8 *) (eap + 1); if (len < sizeof(*eap) + 1) { - printf("handle_eap_response: too short response data\n"); + wpa_printf(MSG_INFO, "handle_eap_response: too short response data"); return; } @@ -675,6 +717,39 @@ static void handle_eap_response(struct hostapd_data *hapd, } +static void handle_eap_initiate(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ +#ifdef CONFIG_ERP + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + if (len < sizeof(*eap) + 1) { + wpa_printf(MSG_INFO, + "handle_eap_initiate: too short response data"); + return; + } + + data = (u8 *) (eap + 1); + type = data[0]; + + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " + "id=%d len=%d) from STA: EAP Initiate type %u", + eap->code, eap->identifier, be_to_host16(eap->length), + type); + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = TRUE; +#endif /* CONFIG_ERP */ +} + + /* Process incoming EAP packet from Supplicant */ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len) @@ -683,7 +758,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u16 eap_len; if (len < sizeof(*eap)) { - printf(" too short EAP packet\n"); + wpa_printf(MSG_INFO, " too short EAP packet"); return; } @@ -718,6 +793,13 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, " (failure)"); return; + case EAP_CODE_INITIATE: + wpa_printf(MSG_DEBUG, " (initiate)"); + handle_eap_initiate(hapd, sta, eap, eap_len); + break; + case EAP_CODE_FINISH: + wpa_printf(MSG_DEBUG, " (finish)"); + break; default: wpa_printf(MSG_DEBUG, " (unknown code)"); return; @@ -761,7 +843,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct rsn_pmksa_cache_entry *pmksa; int key_mgmt; - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen && !hapd->conf->wps_state) return; @@ -776,7 +858,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } if (len < sizeof(*hdr)) { - printf(" too short IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); return; } @@ -786,7 +868,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { - printf(" frame too short for this IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet"); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; return; @@ -812,7 +894,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; } - if (!hapd->conf->ieee802_1x && + if (!hapd->conf->ieee802_1x && !hapd->conf->osen && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " "802.1X not enabled and WPS not used"); @@ -832,7 +914,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, return; #ifdef CONFIG_WPS - if (!hapd->conf->ieee802_1x) { + if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) { u32 wflags = sta->flags & (WLAN_STA_WPS | WLAN_STA_WPS2 | WLAN_STA_MAYBE_WPS); @@ -939,8 +1021,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) int key_mgmt; #ifdef CONFIG_WPS - if (hapd->conf->wps_state && hapd->conf->wpa && - (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + if (hapd->conf->wps_state && + ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) || + (sta->flags & WLAN_STA_WPS))) { /* * Need to enable IEEE 802.1X/EAPOL state machines for possible * WPS handshake even if IEEE 802.1X/EAPOL is not used for @@ -950,7 +1033,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_WPS */ - if (!force_1x && !hapd->conf->ieee802_1x) { + if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - " "802.1X not enabled or forced for WPS"); /* @@ -988,7 +1071,8 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_WPS sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; - if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) { + if (!hapd->conf->ieee802_1x && hapd->conf->wps_state && + !(sta->flags & WLAN_STA_WPS2)) { /* * Delay EAPOL frame transmission until a possible WPS STA * initiates the handshake with EAPOL-Start. Only allow the @@ -1127,15 +1211,11 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, if (eap_type >= 0) sm->eap_type_authsrv = eap_type; os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", - eap_type >= 0 ? eap_server_get_name(0, eap_type) : - "??", - eap_type); + eap_server_get_name(0, eap_type), eap_type); break; case EAP_CODE_RESPONSE: os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", - eap_type >= 0 ? eap_server_get_name(0, eap_type) : - "??", - eap_type); + eap_server_get_name(0, eap_type), eap_type); break; case EAP_CODE_SUCCESS: os_strlcpy(buf, "EAP Success", sizeof(buf)); @@ -1191,6 +1271,11 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd, sm->eap_if->aaaEapKeyDataLen = len; sm->eap_if->aaaEapKeyAvailable = TRUE; } + } else { + wpa_printf(MSG_DEBUG, + "MS-MPPE: 1x_get_keys, could not get keys: %p send: %p recv: %p", + keys, keys ? keys->send : NULL, + keys ? keys->recv : NULL); } if (keys) { @@ -1272,13 +1357,10 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, NULL) < 0) return; - identity = os_malloc(len + 1); + identity = (u8 *) dup_binstr(buf, len); if (identity == NULL) return; - os_memcpy(identity, buf, len); - identity[len] = '\0'; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " "User-Name from Access-Accept '%s'", @@ -1317,6 +1399,147 @@ static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, } +#ifdef CONFIG_HS20 + +static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len) +{ + sta->remediation = 1; + os_free(sta->remediation_url); + if (len > 2) { + sta->remediation_url = os_malloc(len); + if (!sta->remediation_url) + return; + sta->remediation_method = pos[0]; + os_memcpy(sta->remediation_url, pos + 1, len - 1); + sta->remediation_url[len - 1] = '\0'; + wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed " + "for " MACSTR " - server method %u URL %s", + MAC2STR(sta->addr), sta->remediation_method, + sta->remediation_url); + } else { + sta->remediation_url = NULL; + wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed " + "for " MACSTR, MAC2STR(sta->addr)); + } + /* TODO: assign the STA into remediation VLAN or add filtering */ +} + + +static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len) +{ + if (len < 3) + return; /* Malformed information */ + sta->hs20_deauth_requested = 1; + wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u " + "Re-auth Delay %u", + *pos, WPA_GET_LE16(pos + 1)); + wpabuf_free(sta->hs20_deauth_req); + sta->hs20_deauth_req = wpabuf_alloc(len + 1); + if (sta->hs20_deauth_req) { + wpabuf_put_data(sta->hs20_deauth_req, pos, 3); + wpabuf_put_u8(sta->hs20_deauth_req, len - 3); + wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3); + } + ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout); +} + + +static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len, int session_timeout) +{ + unsigned int swt; + int warning_time, beacon_int; + + if (len < 1) + return; /* Malformed information */ + os_free(sta->hs20_session_info_url); + sta->hs20_session_info_url = os_malloc(len); + if (sta->hs20_session_info_url == NULL) + return; + swt = pos[0]; + os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1); + sta->hs20_session_info_url[len - 1] = '\0'; + wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u " + "(session_timeout=%d)", + sta->hs20_session_info_url, swt, session_timeout); + if (session_timeout < 0) { + wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL"); + return; + } + if (swt == 255) + swt = 1; /* Use one minute as the AP selected value */ + + if ((unsigned int) session_timeout < swt * 60) + warning_time = 0; + else + warning_time = session_timeout - swt * 60; + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128; + if (sta->hs20_disassoc_timer > 65535) + sta->hs20_disassoc_timer = 65535; + + ap_sta_session_warning_timeout(hapd, sta, warning_time); +} + +#endif /* CONFIG_HS20 */ + + +static void ieee802_1x_check_hs20(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg, + int session_timeout) +{ +#ifdef CONFIG_HS20 + u8 *buf, *pos, *end, type, sublen; + size_t len; + + buf = NULL; + sta->remediation = 0; + sta->hs20_deauth_requested = 0; + + for (;;) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + break; + if (len < 6) + continue; + pos = buf; + end = buf + len; + if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) + continue; + pos += 4; + + type = *pos++; + sublen = *pos++; + if (sublen < 2) + continue; /* invalid length */ + sublen -= 2; /* skip header */ + if (pos + sublen > end) + continue; /* invalid WFA VSA */ + + switch (type) { + case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION: + ieee802_1x_hs20_sub_rem(sta, pos, sublen); + break; + case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ: + ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen); + break; + case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL: + ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, + session_timeout); + break; + } + } +#endif /* CONFIG_HS20 */ +} + + struct sta_id_search { u8 identifier; struct eapol_state_machine *sm; @@ -1391,15 +1614,14 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, "EAP-Message"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) { - printf("Incoming RADIUS packet did not have correct " - "Message-Authenticator - dropped\n"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && hdr->code != RADIUS_CODE_ACCESS_REJECT && hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { - printf("Unknown RADIUS message code\n"); + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } @@ -1443,8 +1665,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, sta->vlan_id = radius_msg_get_vlanid(msg); } if (sta->vlan_id > 0 && - hostapd_get_vlan_id_ifname(hapd->conf->vlan, - sta->vlan_id)) { + hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1463,6 +1684,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) break; + sta->session_timeout_set = !!session_timeout_set; + sta->session_timeout = session_timeout; + /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { @@ -1477,7 +1701,11 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); ieee802_1x_update_sta_cui(hapd, sta, msg); - if (sm->eap_if->eapKeyAvailable && + ieee802_1x_check_hs20(hapd, sta, msg, + session_timeout_set ? + (int) session_timeout : -1); + if (sm->eap_if->eapKeyAvailable && !sta->remediation && + !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? (int) session_timeout : -1, sm) == 0) { @@ -1564,7 +1792,7 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) if (eapol->default_wep_key == NULL || random_get_bytes(eapol->default_wep_key, hapd->conf->default_wep_key_len)) { - printf("Could not generate random WEP key.\n"); + wpa_printf(MSG_INFO, "Could not generate random WEP key"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; return -1; @@ -1680,14 +1908,14 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, - int preauth) + int preauth, int remediation) { struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; if (preauth) rsn_preauth_finished(hapd, sta, success); else - ieee802_1x_finished(hapd, sta, success); + ieee802_1x_finished(hapd, sta, success, remediation); } @@ -1720,7 +1948,9 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, user->password_hash = eap_user->password_hash; } user->force_version = eap_user->force_version; + user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; + user->remediation = eap_user->remediation; return 0; } @@ -1804,12 +2034,43 @@ static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, } +#ifdef CONFIG_ERP + +static struct eap_server_erp_key * +ieee802_1x_erp_get_key(void *ctx, const char *keyname) +{ + struct hostapd_data *hapd = ctx; + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} + + +static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct hostapd_data *hapd = ctx; + + dl_list_add(&hapd->erp_keys, &erp->list); + return 0; +} + +#endif /* CONFIG_ERP */ + + int ieee802_1x_init(struct hostapd_data *hapd) { int i; struct eapol_auth_config conf; struct eapol_auth_cb cb; + dl_list_init(&hapd->erp_keys); + os_memset(&conf, 0, sizeof(conf)); conf.ctx = hapd; conf.eap_reauth_period = hapd->conf->eap_reauth_period; @@ -1821,6 +2082,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_sim_db_priv = hapd->eap_sim_db_priv; conf.eap_req_id_text = hapd->conf->eap_req_id_text; conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; + conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start; + conf.erp_domain = hapd->conf->erp_domain; + conf.erp = hapd->conf->eap_server_erp; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -1834,6 +2098,13 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.fragment_size = hapd->conf->fragment_size; conf.pwd_group = hapd->conf->pwd_group; conf.pbc_in_m1 = hapd->conf->pbc_in_m1; + if (hapd->conf->server_id) { + conf.server_id = (const u8 *) hapd->conf->server_id; + conf.server_id_len = os_strlen(hapd->conf->server_id); + } else { + conf.server_id = (const u8 *) "hostapd"; + conf.server_id_len = 7; + } os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; @@ -1846,6 +2117,10 @@ int ieee802_1x_init(struct hostapd_data *hapd) cb.abort_auth = _ieee802_1x_abort_auth; cb.tx_key = _ieee802_1x_tx_key; cb.eapol_event = ieee802_1x_eapol_event; +#ifdef CONFIG_ERP + cb.erp_get_key = ieee802_1x_erp_get_key; + cb.erp_add_key = ieee802_1x_erp_add_key; +#endif /* CONFIG_ERP */ hapd->eapol_auth = eapol_auth_init(&conf, &cb); if (hapd->eapol_auth == NULL) @@ -1877,6 +2152,18 @@ int ieee802_1x_init(struct hostapd_data *hapd) } +void ieee802_1x_erp_flush(struct hostapd_data *hapd) +{ + struct eap_server_erp_key *erp; + + while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key, + list)) != NULL) { + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); + } +} + + void ieee802_1x_deinit(struct hostapd_data *hapd) { eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); @@ -1887,6 +2174,8 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; + + ieee802_1x_erp_flush(hapd); } @@ -2061,7 +2350,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; - struct os_time t; + struct os_reltime diff; + const char *name1; + const char *name2; if (sm == NULL) return 0; @@ -2075,7 +2366,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sta->aid, EAPOL_VERSION, sm->initialize); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2103,7 +2394,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->reAuthPeriod, bool_txt(sm->reAuthEnabled), bool_txt(sm->keyTxEnabled)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2133,7 +2424,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->dot1xAuthEapLengthErrorFramesRx, sm->dot1xAuthLastEapolFrameVersion, MAC2STR(sm->addr)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2171,12 +2462,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->backendOtherRequestsToSupplicant, sm->backendAuthSuccesses, sm->backendAuthFails); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; /* dot1xAuthSessionStatsTable */ - os_get_time(&t); + os_reltime_age(&sta->acct_session_start, &diff); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -2191,9 +2482,30 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, - (unsigned int) (t.sec - sta->acct_session_start), + (unsigned int) diff.sec, sm->identity); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + if (sm->acct_multi_session_id_hi) { + ret = os_snprintf(buf + len, buflen - len, + "authMultiSessionId=%08X+%08X\n", + sm->acct_multi_session_id_hi, + sm->acct_multi_session_id_lo); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + name1 = eap_server_get_name(0, sm->eap_type_authsrv); + name2 = eap_server_get_name(0, sm->eap_type_supp); + ret = os_snprintf(buf + len, buflen - len, + "last_eap_type_as=%d (%s)\n" + "last_eap_type_sta=%d (%s)\n", + sm->eap_type_authsrv, name1, + sm->eap_type_supp, name2); + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2202,16 +2514,55 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, static void ieee802_1x_finished(struct hostapd_data *hapd, - struct sta_info *sta, int success) + struct sta_info *sta, int success, + int remediation) { const u8 *key; size_t len; /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; + unsigned int session_timeout; + +#ifdef CONFIG_HS20 + if (remediation && !sta->remediation) { + sta->remediation = 1; + os_free(sta->remediation_url); + sta->remediation_url = + os_strdup(hapd->conf->subscr_remediation_url); + sta->remediation_method = 1; /* SOAP-XML SPP */ + } + + if (success) { + if (sta->remediation) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " + "to " MACSTR " to indicate Subscription " + "Remediation", + MAC2STR(sta->addr)); + hs20_send_wnm_notification(hapd, sta->addr, + sta->remediation_method, + sta->remediation_url); + os_free(sta->remediation_url); + sta->remediation_url = NULL; + } + + if (sta->hs20_deauth_req) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " + "to " MACSTR " to indicate imminent " + "deauthentication", MAC2STR(sta->addr)); + hs20_send_wnm_notification_deauth_req( + hapd, sta->addr, sta->hs20_deauth_req); + } + } +#endif /* CONFIG_HS20 */ key = ieee802_1x_get_key(sta->eapol_sm, &len); - if (success && key && len >= PMK_LEN && - wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + if (sta->session_timeout_set) + session_timeout = sta->session_timeout; + else + session_timeout = dot11RSNAConfigPMKLifetime; + if (success && key && len >= PMK_LEN && !sta->remediation && + !sta->hs20_deauth_requested && + wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, @@ -2238,5 +2589,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, os_sleep(0, 10000); ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_IEEE_802_1X_AUTH_FAILED); + hostapd_wps_eap_completed(hapd); } } diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index e1df94057de7..de6e0e75fac3 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -29,6 +29,7 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_erp_flush(struct hostapd_data *hapd); void ieee802_1x_deinit(struct hostapd_data *hapd); int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack); diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c new file mode 100644 index 000000000000..b0d42dcd82c4 --- /dev/null +++ b/src/ap/ndisc_snoop.c @@ -0,0 +1,171 @@ +/* + * Neighbor Discovery snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include + +#include "utils/common.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "list.h" +#include "x_snoop.h" + +struct ip6addr { + struct in6_addr addr; + struct dl_list list; +}; + +struct icmpv6_ndmsg { + struct ip6_hdr ipv6h; + struct icmp6_hdr icmp6h; + struct in6_addr target_addr; + u8 opt_type; + u8 len; + u8 opt_lladdr[0]; +} STRUCT_PACKED; + +#define ROUTER_ADVERTISEMENT 134 +#define NEIGHBOR_SOLICITATION 135 +#define NEIGHBOR_ADVERTISEMENT 136 +#define SOURCE_LL_ADDR 1 + +static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr) +{ + struct ip6addr *ip6addr; + + ip6addr = os_zalloc(sizeof(*ip6addr)); + if (!ip6addr) + return -1; + + os_memcpy(&ip6addr->addr, addr, sizeof(*addr)); + + dl_list_add_tail(&sta->ip6addr, &ip6addr->list); + + return 0; +} + + +void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct ip6addr *ip6addr, *prev; + + dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr, + list) { + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr); + os_free(ip6addr); + } +} + + +static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) +{ + struct ip6addr *ip6addr; + + dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) { + if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] && + ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] && + ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] && + ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3]) + return 1; + } + + return 0; +} + + +static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + struct icmpv6_ndmsg *msg; + struct in6_addr *saddr; + struct sta_info *sta; + int res; + char addrtxt[INET6_ADDRSTRLEN + 1]; + + if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + return; + msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN]; + switch (msg->icmp6h.icmp6_type) { + case NEIGHBOR_SOLICITATION: + if (len < ETH_HLEN + sizeof(*msg)) + return; + if (msg->opt_type != SOURCE_LL_ADDR) + return; + + saddr = &msg->ipv6h.ip6_src; + if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && + saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { + if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) + return; + sta = ap_get_sta(hapd, msg->opt_lladdr); + if (!sta) + return; + + if (sta_has_ip6addr(sta, saddr)) + return; + + if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt)) + == NULL) + addrtxt[0] = '\0'; + wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for " + MACSTR, addrtxt, MAC2STR(sta->addr)); + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); + res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, + 128, sta->addr); + if (res) { + wpa_printf(MSG_ERROR, + "ndisc_snoop: Adding ip neigh failed: %d", + res); + return; + } + + if (sta_ip6addr_add(sta, saddr)) + return; + } + break; + case ROUTER_ADVERTISEMENT: + if (!hapd->conf->disable_dgaf) + return; + /* fall through */ + case NEIGHBOR_ADVERTISEMENT: + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, + (u8 *) buf, len); + } + break; + default: + break; + } +} + + +int ndisc_snoop_init(struct hostapd_data *hapd) +{ + hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc, + L2_PACKET_FILTER_NDISC); + if (hapd->sock_ndisc == NULL) { + wpa_printf(MSG_DEBUG, + "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void ndisc_snoop_deinit(struct hostapd_data *hapd) +{ + l2_packet_deinit(hapd->sock_ndisc); +} diff --git a/src/ap/ndisc_snoop.h b/src/ap/ndisc_snoop.h new file mode 100644 index 000000000000..3cc9a557b06b --- /dev/null +++ b/src/ap/ndisc_snoop.h @@ -0,0 +1,36 @@ +/* + * Neighbor Discovery snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NDISC_SNOOP_H +#define NDISC_SNOOP_H + +#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6) + +int ndisc_snoop_init(struct hostapd_data *hapd); +void ndisc_snoop_deinit(struct hostapd_data *hapd); +void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta); + +#else /* CONFIG_PROXYARP && CONFIG_IPV6 */ + +static inline int ndisc_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void ndisc_snoop_deinit(struct hostapd_data *hapd) +{ +} + +static inline void sta_ip6addr_del(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */ + +#endif /* NDISC_SNOOP_H */ diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c index 795d313b8c16..9be640cb4622 100644 --- a/src/ap/p2p_hostapd.c +++ b/src/ap/p2p_hostapd.c @@ -96,9 +96,8 @@ u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid) u8 bitmap; *eid++ = WLAN_EID_VENDOR_SPECIFIC; *eid++ = 4 + 3 + 1; - WPA_PUT_BE24(eid, OUI_WFA); - eid += 3; - *eid++ = P2P_OUI_TYPE; + WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE); + eid += 4; *eid++ = P2P_ATTR_MANAGEABILITY; WPA_PUT_LE16(eid, 1); diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c index ba5c606442f4..efc1d7e4c78f 100644 --- a/src/ap/peerkey_auth.c +++ b/src/ap/peerkey_auth.c @@ -79,15 +79,15 @@ static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key) + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 *buf, *pos; size_t buf_len; - if (wpa_parse_kde_ies((const u8 *) (key + 1), - WPA_GET_BE16(key->key_data_length), &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); return; } @@ -221,8 +221,8 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, return; /* Peer RSN IE */ - os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); - pos = buf + kde->rsn_ie_len; + os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len); + pos += kde->rsn_ie_len; /* Peer MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); @@ -253,14 +253,14 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key) + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; - if (wpa_parse_kde_ies((const u8 *) (key + 1), - WPA_GET_BE16(key->key_data_length), &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); return; } @@ -324,15 +324,15 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key) + struct wpa_state_machine *sm, + const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; struct rsn_error_kde error; u16 mui, error_type; - if (wpa_parse_kde_ies((const u8 *) (key + 1), - WPA_GET_BE16(key->key_data_length), &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); return; } diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index d27fd302ba96..877affe4eadc 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -1,6 +1,6 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, 2012, Jouni Malinen + * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" +#include "radius/radius_das.h" #include "sta_info.h" #include "ap_config.h" #include "pmksa_cache_auth.h" @@ -37,53 +38,55 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { - if (entry == NULL) - return; os_free(entry->identity); wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS radius_free_class(&entry->radius_class); #endif /* CONFIG_NO_RADIUS */ - os_free(entry); + bin_clear_free(entry, sizeof(*entry)); } -static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, - struct rsn_pmksa_cache_entry *entry) +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; + unsigned int hash; pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx); - pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + + /* unlink from hash list */ + hash = PMKID_HASH(entry->pmkid); + pos = pmksa->pmkid[hash]; prev = NULL; while (pos) { if (pos == entry) { - if (prev != NULL) { - prev->hnext = pos->hnext; - } else { - pmksa->pmkid[PMKID_HASH(entry->pmkid)] = - pos->hnext; - } + if (prev != NULL) + prev->hnext = entry->hnext; + else + pmksa->pmkid[hash] = entry->hnext; break; } prev = pos; pos = pos->hnext; } + /* unlink from entry list */ pos = pmksa->pmksa; prev = NULL; while (pos) { if (pos == entry) { if (prev != NULL) - prev->next = pos->next; + prev->next = entry->next; else - pmksa->pmksa = pos->next; + pmksa->pmksa = entry->next; break; } prev = pos; pos = pos->next; } + _pmksa_cache_free_entry(entry); } @@ -91,9 +94,9 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " MACSTR, MAC2STR(pmksa->pmksa->spa)); @@ -107,12 +110,12 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; - struct os_time now; + struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); if (pmksa->pmksa == NULL) return; - os_get_time(&now); + os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; @@ -144,6 +147,9 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, entry->eap_type_authsrv = eapol->eap_type_authsrv; entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; + + entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; + entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; } @@ -181,6 +187,9 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, eapol->eap_type_authsrv = entry->eap_type_authsrv; ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; + + eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; + eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; } @@ -188,6 +197,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; + int hash; /* Add the new entry; order by expiration time */ pos = pmksa->pmksa; @@ -205,8 +215,10 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, entry->next = prev->next; prev->next = entry; } - entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; - pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + hash = PMKID_HASH(entry->pmkid); + entry->hnext = pmksa->pmkid[hash]; + pmksa->pmkid[hash] = entry; pmksa->pmksa_count++; if (prev == NULL) @@ -222,6 +234,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_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 * @session_timeout: Session timeout @@ -237,23 +251,32 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *aa, const u8 *spa, int session_timeout, - struct eapol_state_machine *eapol, int akmp) + const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos; - struct os_time now; + 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; - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); - os_get_time(&now); + 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; if (session_timeout > 0) entry->expiration += session_timeout; @@ -342,6 +365,8 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) _pmksa_cache_free_entry(prev); } eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + pmksa->pmksa_count = 0; + pmksa->pmksa = NULL; for (i = 0; i < PMKID_HASH_SIZE; i++) pmksa->pmkid[i] = NULL; os_free(pmksa); @@ -361,18 +386,22 @@ pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, { struct rsn_pmksa_cache_entry *entry; - if (pmkid) - entry = pmksa->pmkid[PMKID_HASH(pmkid)]; - else - entry = pmksa->pmksa; - while (entry) { - if ((spa == NULL || - os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && - (pmkid == NULL || - os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = pmkid ? entry->hnext : entry->next; + if (pmkid) { + for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry; + entry = entry->hnext) { + if ((spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) + return entry; + } + } else { + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) + return entry; + } } + return NULL; } @@ -394,15 +423,13 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( struct rsn_pmksa_cache_entry *entry; u8 new_pmkid[PMKID_LEN]; - entry = pmksa->pmksa; - while (entry) { + for (entry = pmksa->pmksa; entry; entry = entry->next) { if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) continue; rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, wpa_key_mgmt_sha256(entry->akmp)); if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) return entry; - entry = entry->next; } return NULL; } @@ -428,3 +455,74 @@ pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, return pmksa; } + + +static int das_attr_match(struct rsn_pmksa_cache_entry *entry, + struct radius_das_attrs *attr) +{ + int match = 0; + + if (attr->sta_addr) { + if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0) + return 0; + match++; + } + + if (attr->acct_multi_session_id) { + char buf[20]; + + if (attr->acct_multi_session_id_len != 17) + return 0; + os_snprintf(buf, sizeof(buf), "%08X+%08X", + entry->acct_multi_session_id_hi, + entry->acct_multi_session_id_lo); + if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0) + return 0; + match++; + } + + if (attr->cui) { + if (!entry->cui || + attr->cui_len != wpabuf_len(entry->cui) || + os_memcmp(attr->cui, wpabuf_head(entry->cui), + attr->cui_len) != 0) + return 0; + match++; + } + + if (attr->user_name) { + if (!entry->identity || + attr->user_name_len != entry->identity_len || + os_memcmp(attr->user_name, entry->identity, + attr->user_name_len) != 0) + return 0; + match++; + } + + return match; +} + + +int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, + struct radius_das_attrs *attr) +{ + int found = 0; + struct rsn_pmksa_cache_entry *entry, *prev; + + if (attr->acct_session_id) + return -1; + + entry = pmksa->pmksa; + while (entry) { + if (das_attr_match(entry, attr)) { + found++; + prev = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, prev); + continue; + } + entry = entry->next; + } + + return found ? 0 : -1; +} diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index d473f3fdced3..8b7be1291b53 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -30,6 +30,9 @@ struct rsn_pmksa_cache_entry { u8 eap_type_authsrv; int vlan_id; int opportunistic; + + u32 acct_multi_session_id_hi; + u32 acct_multi_session_id_lo; }; struct rsn_pmksa_cache; @@ -47,6 +50,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( struct rsn_pmksa_cache_entry * pmksa_cache_auth_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, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * @@ -55,5 +59,9 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid); void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol); +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); +int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, + struct radius_das_attrs *attr); #endif /* PMKSA_CACHE_H */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 97cd0136b86d..7e75e1a61e1a 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,9 +12,9 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/sae.h" #include "radius/radius.h" #include "radius/radius_client.h" -#include "drivers/driver.h" #include "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" @@ -30,11 +30,14 @@ #include "p2p_hostapd.h" #include "ap_drv_ops.h" #include "gas_serv.h" +#include "wnm_ap.h" +#include "ndisc_snoop.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx); static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx); static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W @@ -69,6 +72,30 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) } +#ifdef CONFIG_P2P +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + const u8 *p2p_dev_addr; + + if (sta->p2p_ie == NULL) + continue; + + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + if (p2p_dev_addr == NULL) + continue; + + if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0) + return sta; + } + + return NULL; +} +#endif /* CONFIG_P2P */ + + static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *tmp; @@ -118,6 +145,12 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) } +void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta_ip6addr_del(hapd, sta); +} + + void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { int set_beacon = 0; @@ -128,9 +161,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) ap_sta_set_authorized(hapd, sta, 0); if (sta->flags & WLAN_STA_WDS) - hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0); + hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); + + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); - if (!(sta->flags & WLAN_STA_PREAUTH)) + if (!hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); ap_sta_hash_del(hapd, sta); @@ -179,6 +217,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_IEEE80211N + ht40_intolerant_remove(hapd->iface, sta); +#endif /* CONFIG_IEEE80211N */ + #ifdef CONFIG_P2P if (sta->no_p2p_set) { sta->no_p2p_set = 0; @@ -193,6 +235,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) set_beacon++; #endif /* NEED_AP_MLME && CONFIG_IEEE80211N */ +#ifdef CONFIG_MESH + if (hapd->mesh_sta_free_cb) + hapd->mesh_sta_free_cb(sta); +#endif /* CONFIG_MESH */ + if (set_beacon) ieee802_11_set_beacons(hapd->iface); @@ -200,17 +247,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) __func__, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + sae_clear_retransmit_timer(hapd, sta); ieee802_1x_free_station(sta); wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); #ifndef CONFIG_NO_RADIUS - radius_client_flush_auth(hapd->radius, sta->addr); + if (hapd->radius) + radius_client_flush_auth(hapd->radius, sta->addr); #endif /* CONFIG_NO_RADIUS */ - os_free(sta->last_assoc_req); os_free(sta->challenge); #ifdef CONFIG_IEEE80211W @@ -236,9 +285,18 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->hs20_ie); os_free(sta->ht_capabilities); + os_free(sta->vht_capabilities); hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); + os_free(sta->remediation_url); + wpabuf_free(sta->hs20_deauth_req); + os_free(sta->hs20_session_info_url); + +#ifdef CONFIG_SAE + sae_clear_data(sta->sae); + os_free(sta->sae); +#endif /* CONFIG_SAE */ os_free(sta); } @@ -277,6 +335,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; unsigned long next_time = 0; + int reason; wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", __func__, MAC2STR(sta->addr), sta->flags, @@ -311,8 +370,15 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) * but do not disconnect the station now. */ next_time = hapd->conf->ap_max_inactivity + fuzz; - } else if (inactive_sec < hapd->conf->ap_max_inactivity && - sta->flags & WLAN_STA_ASSOC) { + } else if (inactive_sec == -ENOENT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has lost its driver entry", + MAC2STR(sta->addr)); + + /* Avoid sending client probe on removed client */ + sta->timeout_next = STA_DISASSOC; + goto skip_poll; + } else if (inactive_sec < hapd->conf->ap_max_inactivity) { /* station activity detected; reset timeout state */ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " has been active %is ago", @@ -344,6 +410,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) next_time = hapd->conf->ap_max_inactivity; } +skip_poll: if (next_time) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%lu seconds)", @@ -372,9 +439,11 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } else { - hostapd_drv_sta_disassoc( - hapd, sta->addr, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + reason = (sta->timeout_next == STA_DISASSOC) ? + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; + + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); } } @@ -388,6 +457,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) hapd, sta); break; case STA_DISASSOC: + case STA_DISASSOC_FROM_CLI: ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~WLAN_STA_ASSOC; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); @@ -399,14 +469,16 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); + reason = (sta->timeout_next == STA_DISASSOC) ? + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)", __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); - mlme_disassociate_indication( - hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + mlme_disassociate_indication(hapd, sta, reason); break; case STA_DEAUTH: case STA_REMOVE: @@ -429,7 +501,6 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; - u8 addr[ETH_ALEN]; if (!(sta->flags & WLAN_STA_AUTH)) { if (sta->flags & WLAN_STA_GAS) { @@ -440,6 +511,8 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) return; } + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); mlme_deauthenticate_indication(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -447,9 +520,19 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) "session timeout"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; - os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); - hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ + if (eloop_replenish_timeout(session_timeout, 0, + ap_handle_session_timer, hapd, sta) == 1) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout " + "to %d seconds", session_timeout); + } } @@ -471,6 +554,32 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) } +static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) +{ +#ifdef CONFIG_WNM + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR, + MAC2STR(sta->addr)); + if (sta->hs20_session_info_url == NULL) + return; + + wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, + sta->hs20_disassoc_timer); +#endif /* CONFIG_WNM */ +} + + +void ap_sta_session_warning_timeout(struct hostapd_data *hapd, + struct sta_info *sta, int warning_time) +{ + eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); + eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer, + hapd, sta); +} + + struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; @@ -495,13 +604,16 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) sta->acct_interim_interval = hapd->conf->acct_interim_interval; accounting_sta_get_id(hapd, sta); + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(addr), + hapd->conf->ap_max_inactivity); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + } + /* initialize STA info data */ - wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " - "for " MACSTR " (%d seconds - ap_max_inactivity)", - __func__, MAC2STR(addr), - hapd->conf->ap_max_inactivity); - eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, - ap_handle_timer, hapd, sta); os_memcpy(sta->addr, addr, ETH_ALEN); sta->next = hapd->sta_list; hapd->sta_list = sta; @@ -509,6 +621,8 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) ap_sta_hash_add(hapd, sta); sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + dl_list_init(&sta->ip6addr); return sta; } @@ -518,6 +632,10 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) { ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); + wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); if (hostapd_drv_sta_remove(hapd, sta->addr) && @@ -570,7 +688,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); - sta->flags &= ~WLAN_STA_ASSOC; + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " @@ -608,7 +727,8 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, { wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " @@ -663,13 +783,6 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (sta->vlan_id == old_vlanid) return 0; - /* - * During 1x reauth, if the vlan id changes, then remove the old id and - * proceed furthur to add the new one. - */ - if (old_vlanid > 0) - vlan_remove_dynamic(hapd, old_vlanid); - iface = hapd->conf->iface; if (sta->ssid->vlan[0]) iface = sta->ssid->vlan; @@ -677,15 +790,19 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; else if (sta->vlan_id > 0) { + struct hostapd_vlan *wildcard_vlan = NULL; vlan = hapd->conf->vlan; while (vlan) { - if (vlan->vlan_id == sta->vlan_id || - vlan->vlan_id == VLAN_ID_WILDCARD) { - iface = vlan->ifname; + if (vlan->vlan_id == sta->vlan_id) break; - } + if (vlan->vlan_id == VLAN_ID_WILDCARD) + wildcard_vlan = vlan; vlan = vlan->next; } + if (!vlan) + vlan = wildcard_vlan; + if (vlan) + iface = vlan->ifname; } if (sta->vlan_id > 0 && vlan == NULL) { @@ -693,7 +810,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " "binding station to (vlan_id=%d)", sta->vlan_id); - return -1; + ret = -1; + goto done; } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); if (vlan == NULL) { @@ -702,7 +820,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "could not add " "dynamic VLAN interface for vlan_id=%d", sta->vlan_id); - return -1; + ret = -1; + goto done; } iface = vlan->ifname; @@ -756,6 +875,12 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, HOSTAPD_LEVEL_DEBUG, "could not bind the STA " "entry to vlan_id=%d", sta->vlan_id); } + +done: + /* During 1x reauth, if the vlan id changes, then remove the old id. */ + if (old_vlanid > 0) + vlan_remove_dynamic(hapd, old_vlanid); + return ret; #else /* CONFIG_NO_VLAN */ return 0; @@ -768,9 +893,9 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) { u32 tu; - struct os_time now, passed; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); + struct os_reltime now, passed; + 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) { hostapd_logger(hapd, sta->addr, @@ -807,13 +932,21 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) return; if (sta->sa_query_count == 0) { /* Starting a new SA Query procedure */ - os_get_time(&sta->sa_query_start); + os_get_reltime(&sta->sa_query_start); } trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; sta->sa_query_trans_id = nbuf; sta->sa_query_count++; - os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { + /* + * We don't really care which ID is used here, so simply + * hardcode this if the mostly theoretical os_get_random() + * failure happens. + */ + trans_id[0] = 0x12; + trans_id[1] = 0x34; + } timeout = hapd->conf->assoc_sa_query_retry_timeout; sec = ((timeout / 1000) * 1024) / 1000; @@ -849,13 +982,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { const u8 *dev_addr = NULL; + char buf[100]; #ifdef CONFIG_P2P u8 addr[ETH_ALEN]; + u8 ip_addr_buf[4]; #endif /* CONFIG_P2P */ if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) return; + if (authorized) + sta->flags |= WLAN_STA_AUTHORIZED; + else + sta->flags &= ~WLAN_STA_AUTHORIZED; + #ifdef CONFIG_P2P if (hapd->p2p_group == NULL) { if (sta->p2p_ie != NULL && @@ -863,52 +1003,46 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, dev_addr = addr; } else dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); + + if (dev_addr) + os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else #endif /* CONFIG_P2P */ + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr)); + + if (hapd->sta_authorized_cb) + hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, + sta->addr, authorized, dev_addr); if (authorized) { - if (dev_addr) - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED - MACSTR " p2p_dev_addr=" MACSTR, - MAC2STR(sta->addr), MAC2STR(dev_addr)); - else - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED - MACSTR, MAC2STR(sta->addr)); - if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED MACSTR " p2p_dev_addr=" - MACSTR, - MAC2STR(sta->addr), MAC2STR(dev_addr)); - else if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + char ip_addr[100]; + ip_addr[0] = '\0'; +#ifdef CONFIG_P2P + if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { + os_snprintf(ip_addr, sizeof(ip_addr), + " ip_addr=%u.%u.%u.%u", + ip_addr_buf[0], ip_addr_buf[1], + ip_addr_buf[2], ip_addr_buf[3]); + } +#endif /* CONFIG_P2P */ - sta->flags |= WLAN_STA_AUTHORIZED; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", + buf, ip_addr); + + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_CONNECTED "%s%s", + buf, ip_addr); } else { - if (dev_addr) - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED - MACSTR " p2p_dev_addr=" MACSTR, - MAC2STR(sta->addr), MAC2STR(dev_addr)); - else - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED - MACSTR, MAC2STR(sta->addr)); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); + if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_DISCONNECTED MACSTR " p2p_dev_addr=" - MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr)); - else if (hapd->msg_ctx_parent && - hapd->msg_ctx_parent != hapd->msg_ctx) - wpa_msg(hapd->msg_ctx_parent, MSG_INFO, - AP_STA_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); - sta->flags &= ~WLAN_STA_AUTHORIZED; + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED "%s", buf); } - - if (hapd->sta_authorized_cb) - hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, - sta->addr, authorized, dev_addr); } @@ -969,3 +1103,36 @@ void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ap_sta_disassoc_cb_timeout(hapd, sta); } + + +int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) +{ + int res; + + buf[0] = '\0'; + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), + (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), + (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), + (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : + ""), + (flags & WLAN_STA_SHORT_PREAMBLE ? + "[SHORT_PREAMBLE]" : ""), + (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), + (flags & WLAN_STA_WMM ? "[WMM]" : ""), + (flags & WLAN_STA_MFP ? "[MFP]" : ""), + (flags & WLAN_STA_WPS ? "[WPS]" : ""), + (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), + (flags & WLAN_STA_WDS ? "[WDS]" : ""), + (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), + (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), + (flags & WLAN_STA_GAS ? "[GAS]" : ""), + (flags & WLAN_STA_VHT ? "[VHT]" : ""), + (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), + (flags & WLAN_STA_WNM_SLEEP_MODE ? + "[WNM_SLEEP_MODE]" : "")); + if (os_snprintf_error(buflen, res)) + res = -1; + + return res; +} diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index d5e92faa7637..57551ab17d5d 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -9,12 +9,16 @@ #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_PS BIT(2) -#define WLAN_STA_TIM BIT(3) -#define WLAN_STA_PERM BIT(4) #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) @@ -29,6 +33,9 @@ #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) @@ -42,6 +49,8 @@ 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; @@ -50,19 +59,39 @@ struct sta_info { 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; - u8 previous_ap[6]; enum { - STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE, + STA_DISASSOC_FROM_CLI } timeout_next; u16 deauth_reason; @@ -71,12 +100,9 @@ struct sta_info { /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; - /* IEEE 802.11f (IAPP) related data */ - struct ieee80211_mgmt *last_assoc_req; - u32 acct_session_id_hi; u32 acct_session_id_lo; - time_t acct_session_start; + struct os_reltime acct_session_start; int acct_session_started; int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_interim_interval; /* Acct-Interim-Interval */ @@ -103,6 +129,7 @@ struct sta_info { 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; @@ -111,7 +138,7 @@ struct sta_info { 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_time sa_query_start; + struct os_reltime sa_query_start; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_INTERWORKING @@ -123,13 +150,25 @@ struct sta_info { 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; - struct os_time connected_time; + struct os_reltime connected_time; #ifdef CONFIG_SAE - enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state; - u16 sae_send_confirm; + 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; }; @@ -156,14 +195,20 @@ int ap_for_each_sta(struct hostapd_data *hapd, 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); @@ -191,4 +236,6 @@ static inline int ap_sta_is_authorized(struct sta_info *sta) 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/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index 4a2ea0665d88..4725e2b3e8f1 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -68,7 +68,7 @@ void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd) int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) { - struct os_time now; + struct os_reltime now; int ret = 0; if (addr && local) { @@ -89,8 +89,8 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) } } - os_get_time(&now); - if (now.sec > hapd->michael_mic_failure + 60) { + os_get_reltime(&now); + if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) { hapd->michael_mic_failures = 1; } else { hapd->michael_mic_failures++; @@ -99,7 +99,7 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) ret = 1; } } - hapd->michael_mic_failure = now.sec; + hapd->michael_mic_failure = now; return ret; } diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 7b1a9e6711a5..dc6501997db0 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -4,14 +4,8 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -493,8 +487,18 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { - os_snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + if (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, sizeof(br_name), "%s%d", + hapd->conf->vlan_bridge, + vlan->vlan_id); + } else if (tagged_interface) { + os_snprintf(br_name, sizeof(br_name), + "br%s.%d", tagged_interface, + vlan->vlan_id); + } else { + os_snprintf(br_name, sizeof(br_name), + "brvlan%d", vlan->vlan_id); + } if (!br_addbr(br_name)) vlan->clean |= DVLAN_CLEAN_BR; @@ -550,8 +554,18 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) while (vlan) { if (os_strcmp(ifname, vlan->ifname) == 0) { - os_snprintf(br_name, sizeof(br_name), "brvlan%d", - vlan->vlan_id); + if (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, sizeof(br_name), "%s%d", + hapd->conf->vlan_bridge, + vlan->vlan_id); + } else if (tagged_interface) { + os_snprintf(br_name, sizeof(br_name), + "br%s.%d", tagged_interface, + vlan->vlan_id); + } else { + os_snprintf(br_name, sizeof(br_name), + "brvlan%d", vlan->vlan_id); + } if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) br_delif(br_name, vlan->ifname); @@ -603,6 +617,7 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, struct ifinfomsg *ifi; int attrlen, nlmsg_len, rta_len; struct rtattr *attr; + char ifname[IFNAMSIZ + 1]; if (len < sizeof(*ifi)) return; @@ -617,29 +632,39 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + os_memset(ifname, 0, sizeof(ifname)); rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { - char ifname[IFNAMSIZ + 1]; - if (attr->rta_type == IFLA_IFNAME) { int n = attr->rta_len - rta_len; if (n < 0) break; - os_memset(ifname, 0, sizeof(ifname)); - - if ((size_t) n > sizeof(ifname)) - n = sizeof(ifname); + if ((size_t) n >= sizeof(ifname)) + n = sizeof(ifname) - 1; os_memcpy(ifname, ((char *) attr) + rta_len, n); - if (del) - vlan_dellink(ifname, hapd); - else - vlan_newlink(ifname, hapd); } attr = RTA_NEXT(attr, attrlen); } + + if (!ifname[0]) + return; + + wpa_printf(MSG_DEBUG, + "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + del ? "DEL" : "NEW", + ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); } @@ -663,7 +688,7 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) } h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { + while (NLMSG_OK(h, left)) { int len, plen; len = h->nlmsg_len; @@ -684,9 +709,7 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) break; } - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); + h = NLMSG_NEXT(h, left); } if (left > 0) { @@ -837,6 +860,24 @@ int vlan_init(struct hostapd_data *hapd) hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + !hapd->conf->vlan) { + /* dynamic vlans enabled but no (or empty) vlan_file given */ + struct hostapd_vlan *vlan; + vlan = os_zalloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while assigning " + "VLAN interfaces"); + return -1; + } + + vlan->vlan_id = VLAN_ID_WILDCARD; + os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", + hapd->conf->iface); + vlan->next = hapd->conf->vlan; + hapd->conf->vlan = vlan; + } + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) return -1; @@ -850,6 +891,7 @@ void vlan_deinit(struct hostapd_data *hapd) #ifdef CONFIG_FULL_DYNAMIC_VLAN full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); + hapd->full_dynamic_vlan = NULL; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } @@ -858,7 +900,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, int vlan_id) { - struct hostapd_vlan *n; + struct hostapd_vlan *n = NULL; char *ifname, *pos; if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || @@ -871,28 +913,24 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, if (ifname == NULL) return NULL; pos = os_strchr(ifname, '#'); - if (pos == NULL) { - os_free(ifname); - return NULL; - } + if (pos == NULL) + goto free_ifname; *pos++ = '\0'; n = os_zalloc(sizeof(*n)); - if (n == NULL) { - os_free(ifname); - return NULL; - } + if (n == NULL) + goto free_ifname; n->vlan_id = vlan_id; n->dynamic_vlan = 1; os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos); - os_free(ifname); if (hostapd_vlan_if_add(hapd, n->ifname)) { os_free(n); - return NULL; + n = NULL; + goto free_ifname; } n->next = hapd->conf->vlan; @@ -902,6 +940,8 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, ifconfig_up(n->ifname); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ +free_ifname: + os_free(ifname); return n; } diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h index 382d5dee4f80..781eaac441be 100644 --- a/src/ap/vlan_init.h +++ b/src/ap/vlan_init.h @@ -3,14 +3,8 @@ * Copyright 2003, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef VLAN_INIT_H diff --git a/src/ap/wmm.c b/src/ap/wmm.c index d21c82f6de2e..6d4177c2a847 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -4,14 +4,8 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -157,7 +151,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, len = ((u8 *) (t + 1)) - buf; if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0) - perror("wmm_send_action: send"); + wpa_printf(MSG_INFO, "wmm_send_action: send failed"); } diff --git a/src/ap/wmm.h b/src/ap/wmm.h index 96b04e890963..b70b8636038f 100644 --- a/src/ap/wmm.h +++ b/src/ap/wmm.h @@ -3,14 +3,8 @@ * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WME_H diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 54a6b857d784..4c8bc10083c4 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -1,6 +1,6 @@ /* * hostapd - WNM - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,7 +9,9 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ap_config.h" @@ -72,7 +74,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, wnmsleep_ie.len = wnmsleep_ie_len - 2; wnmsleep_ie.action_type = action_type; wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; - wnmsleep_ie.intval = intval; + wnmsleep_ie.intval = host_to_le16(intval); /* TFS IE(s) */ wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); @@ -154,6 +156,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, */ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { + sta->flags |= WLAN_STA_WNM_SLEEP_MODE; hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, addr, NULL, NULL); wpa_set_wnmsleep(sta->wpa_sm, 1); @@ -167,6 +170,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, wnmsleep_ie.status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; wpa_set_wnmsleep(sta->wpa_sm, 0); hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, addr, NULL, NULL); @@ -233,7 +237,7 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, wnmsleep_ie->action_type, - wnmsleep_ie->intval); + le_to_host16(wnmsleep_ie->intval)); if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { /* clear the tfs after sending the resp frame */ @@ -243,29 +247,350 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, } +static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, + const u8 *addr, + u8 dialog_token, + const char *url) +{ + struct ieee80211_mgmt *mgmt; + size_t url_len, len; + u8 *pos; + int res; + + if (url) + url_len = os_strlen(url); + else + url_len = 0; + + mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); + if (mgmt == NULL) + return -1; + 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; + mgmt->u.action.u.bss_tm_req.req_mode = 0; + mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); + mgmt->u.action.u.bss_tm_req.validity_interval = 1; + pos = mgmt->u.action.u.bss_tm_req.variable; + if (url) { + *pos++ += url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + } + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " + MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " + "validity_interval=%u", + MAC2STR(addr), dialog_token, + mgmt->u.action.u.bss_tm_req.req_mode, + le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), + mgmt->u.action.u.bss_tm_req.validity_interval); + + len = pos - &mgmt->u.action.category; + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +{ + u8 dialog_token, reason; + const u8 *pos, *end; + + if (len < 2) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " + MACSTR, MAC2STR(addr)); + return; + } + + pos = frm; + end = pos + len; + dialog_token = *pos++; + reason = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from " + MACSTR " dialog_token=%u reason=%u", + MAC2STR(addr), dialog_token, reason); + + wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", + pos, end - pos); + + ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); +} + + +static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +{ + u8 dialog_token, status_code, bss_termination_delay; + const u8 *pos, *end; + + if (len < 3) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " + MACSTR, MAC2STR(addr)); + return; + } + + pos = frm; + end = pos + len; + dialog_token = *pos++; + status_code = *pos++; + bss_termination_delay = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from " + MACSTR " dialog_token=%u status_code=%u " + "bss_termination_delay=%u", MAC2STR(addr), dialog_token, + status_code, bss_termination_delay); + + if (status_code == WNM_BSS_TM_ACCEPT) { + if (end - pos < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); + return; + } + wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, + MAC2STR(pos)); + wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR + " status_code=%u bss_termination_delay=%u target_bssid=" + MACSTR, + MAC2STR(addr), status_code, bss_termination_delay, + MAC2STR(pos)); + pos += ETH_ALEN; + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR + " status_code=%u bss_termination_delay=%u", + MAC2STR(addr), status_code, bss_termination_delay); + } + + wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", + pos, end - pos); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, - struct rx_action *action) + const struct ieee80211_mgmt *mgmt, size_t len) { - if (action->len < 1 || action->data == NULL) + u8 action; + const u8 *payload; + size_t plen; + + if (len < IEEE80211_HDRLEN + 2) return -1; - switch (action->data[0]) { + payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; + action = *payload++; + plen = len - IEEE80211_HDRLEN - 2; + + switch (action) { case WNM_BSS_TRANS_MGMT_QUERY: - wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query"); - /* TODO */ - return -1; + ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload, + plen); + return 0; case WNM_BSS_TRANS_MGMT_RESP: - wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management " - "Response"); - /* TODO */ - return -1; + ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, + plen); + return 0; case WNM_SLEEP_MODE_REQ: - ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1, - action->len - 1); + ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, - action->data[0], MAC2STR(action->sa)); + action, MAC2STR(mgmt->sa)); return -1; } + + +int wnm_send_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, int disassoc_timer) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->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_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = 0; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " + MACSTR, disassoc_timer, MAC2STR(sta->addr)); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " + "Management Request frame"); + return -1; + } + + return 0; +} + + +static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta, + int disassoc_timer) +{ + int timeout, beacon_int; + + /* + * Prevent STA from reconnecting using cached PMKSA to force + * full authentication with the authentication server (which may + * decide to reject the connection), + */ + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + /* Calculate timeout in ms based on beacon_int in TU */ + timeout = disassoc_timer * beacon_int * 128 / 125; + wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR + " set to %d ms", MAC2STR(sta->addr), timeout); + + sta->timeout_next = STA_DISASSOC_FROM_CLI; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(timeout / 1000, + timeout % 1000 * 1000, + ap_handle_timer, hapd, sta); +} + + +int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, const char *url, + int disassoc_timer) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t url_len; + + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->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_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT | + WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = 0x01; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + /* Session Information URL */ + url_len = os_strlen(url); + if (url_len > 255) + return -1; + *pos++ = url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " + "Management Request frame"); + return -1; + } + + if (disassoc_timer) { + /* send disassociation frame after time-out */ + set_disassoc_timer(hapd, sta, disassoc_timer); + } + + return 0; +} + + +int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 req_mode, int disassoc_timer, u8 valid_int, + const u8 *bss_term_dur, const char *url, + const u8 *nei_rep, size_t nei_rep_len) +{ + u8 *buf, *pos; + struct ieee80211_mgmt *mgmt; + size_t url_len; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " + MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", + MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); + buf = os_zalloc(1000 + nei_rep_len); + if (buf == NULL) + return -1; + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->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_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = req_mode; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = valid_int; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) && + bss_term_dur) { + os_memcpy(pos, bss_term_dur, 12); + pos += 12; + } + + if (url) { + /* Session Information URL */ + url_len = os_strlen(url); + if (url_len > 255) { + os_free(buf); + return -1; + } + + *pos++ = url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + } + + if (nei_rep) { + os_memcpy(pos, nei_rep, nei_rep_len); + pos += nei_rep_len; + } + + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "Failed to send BSS Transition Management Request frame"); + os_free(buf); + return -1; + } + os_free(buf); + + if (disassoc_timer) { + /* send disassociation frame after time-out */ + set_disassoc_timer(hapd, sta, disassoc_timer); + } + + return 0; +} diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index f05726ee74d1..7789307209c9 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -1,6 +1,6 @@ /* * IEEE 802.11v WNM related functions and structures - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,9 +9,18 @@ #ifndef WNM_AP_H #define WNM_AP_H -struct rx_action; +struct sta_info; int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, - struct rx_action *action); + const struct ieee80211_mgmt *mgmt, size_t len); +int wnm_send_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, int disassoc_timer); +int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, const char *url, + int disassoc_timer); +int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 req_mode, int disassoc_timer, u8 valid_int, + const u8 *bss_term_dur, const char *url, + const u8 *nei_rep, size_t nei_rep_len); #endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 2c70149a1818..9c5f6094acbb 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/state_machine.h" +#include "utils/bitfield.h" #include "common/ieee802_11_defs.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" @@ -32,7 +33,8 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, + size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); @@ -41,6 +43,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, + const u8 *pmk, struct wpa_ptk *ptk); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -82,11 +86,14 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, - const u8 *addr, const u8 *prev_psk) + const u8 *addr, + const u8 *p2p_dev_addr, + const u8 *prev_psk) { if (wpa_auth->cb.get_psk == NULL) return NULL; - return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, + prev_psk); } @@ -131,6 +138,17 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, } +#ifdef CONFIG_MESH +static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (wpa_auth->cb.start_ampe == NULL) + return -1; + return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); +} +#endif /* CONFIG_MESH */ + + int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) @@ -207,6 +225,8 @@ static int wpa_use_aes_cmac(struct wpa_state_machine *sm) if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) ret = 1; #endif /* CONFIG_IEEE80211W */ + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) + ret = 1; return ret; } @@ -282,8 +302,9 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { - u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)]; u8 rkey[32]; + unsigned long ptr; if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) return -1; @@ -295,7 +316,8 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); wpa_get_ntp_timestamp(buf + ETH_ALEN); - os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + ptr = (unsigned long) group; + os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; @@ -393,6 +415,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, wpa_auth); if (wpa_auth->pmksa == NULL) { wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + os_free(wpa_auth->group); os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; @@ -402,6 +425,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); + os_free(wpa_auth->group); os_free(wpa_auth->wpa_ie); pmksa_cache_auth_deinit(wpa_auth->pmksa); os_free(wpa_auth); @@ -419,6 +443,17 @@ struct wpa_authenticator * wpa_init(const u8 *addr, wpa_rekey_gtk, wpa_auth, NULL); } +#ifdef CONFIG_P2P + if (WPA_GET_BE32(conf->ip_addr_start)) { + int count = WPA_GET_BE32(conf->ip_addr_end) - + WPA_GET_BE32(conf->ip_addr_start) + 1; + if (count > 1000) + count = 1000; + if (count > 0) + wpa_auth->ip_pool = bitfield_alloc(count); + } +#endif /* CONFIG_P2P */ + return wpa_auth; } @@ -432,6 +467,8 @@ int wpa_init_keys(struct wpa_authenticator *wpa_auth) wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; wpa_group_sm_step(wpa_auth, group); + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; return 0; } @@ -459,6 +496,11 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) wpa_auth->ft_pmk_cache = NULL; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + bitfield_free(wpa_auth->ip_pool); +#endif /* CONFIG_P2P */ + + os_free(wpa_auth->wpa_ie); group = wpa_auth->group; @@ -506,14 +548,20 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr) { struct wpa_state_machine *sm; + if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return NULL; + sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); + if (p2p_dev_addr) + os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; @@ -533,6 +581,8 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "FT authentication already completed - do not " "start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; return 0; } #endif /* CONFIG_IEEE80211R */ @@ -570,12 +620,26 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) static void wpa_free_sta_sm(struct wpa_state_machine *sm) { +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr)) { + u32 start; + wpa_printf(MSG_DEBUG, "P2P: Free assigned IP " + "address %u.%u.%u.%u from " MACSTR, + sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start); + bitfield_clear(sm->wpa_auth->ip_pool, + WPA_GET_BE32(sm->ip_addr) - start); + } +#endif /* CONFIG_P2P */ if (sm->GUpdateStationKeys) { sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ftie); + wpabuf_free(sm->ft_pending_req_ies); #endif /* CONFIG_IEEE80211R */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); @@ -734,40 +798,96 @@ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, } +static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, + size_t data_len) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + for (;;) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, + sm->p2p_dev_addr, pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); + + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) + == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + break; + } + + if (!ok) { + wpa_printf(MSG_DEBUG, + "WPA: Earlier SNonce did not result in matching MIC"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "WPA: Earlier SNonce resulted in matching MIC"); + sm->alt_snonce_valid = 0; + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; + + return 0; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; + struct wpa_eapol_key_192 *key192; u16 key_info, key_data_length; enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; struct wpa_eapol_ie_parse kde; int ft; - const u8 *eapol_key_ie; - size_t eapol_key_ie_len; + const u8 *eapol_key_ie, *key_data; + size_t eapol_key_ie_len, keyhdrlen, mic_len; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; - if (data_len < sizeof(*hdr) + sizeof(*key)) + mic_len = wpa_mic_len(sm->wpa_key_mgmt); + keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + + if (data_len < sizeof(*hdr) + keyhdrlen) return; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); + key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); - key_data_length = WPA_GET_BE16(key->key_data_length); + if (mic_len == 24) { + key_data = (const u8 *) (key192 + 1); + key_data_length = WPA_GET_BE16(key192->key_data_length); + } else { + key_data = (const u8 *) (key + 1); + key_data_length = WPA_GET_BE16(key->key_data_length); + } wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR " key_info=0x%x type=%u key_data_length=%u", MAC2STR(sm->addr), key_info, key->type, key_data_length); - if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { + if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", key_data_length, (unsigned long) (data_len - sizeof(*hdr) - - sizeof(*key))); + keyhdrlen)); return; } @@ -835,6 +955,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, if (sm->pairwise == WPA_CIPHER_CCMP || sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && + sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -853,6 +975,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } } + + if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); + return; + } } if (key_info & WPA_KEY_INFO_REQUEST) { @@ -888,8 +1017,25 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, "based on retransmitted EAPOL-Key " "1/4"); sm->update_snonce = 1; - wpa_replay_counter_mark_invalid(sm->prev_key_replay, - key->replay_counter); + os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); + sm->alt_snonce_valid = TRUE; + os_memcpy(sm->alt_replay_counter, + sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + goto continue_processing; + } + + if (msg == PAIRWISE_4 && sm->alt_snonce_valid && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(key->replay_counter, sm->alt_replay_counter, + WPA_REPLAY_COUNTER_LEN) == 0) { + /* + * Supplicant may still be using the old SNonce since + * there was two EAPOL-Key 2/4 messages and they had + * different SNonce values. + */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); goto continue_processing; } @@ -948,8 +1094,7 @@ continue_processing: wpa_sta_disconnect(wpa_auth, sm->addr); return; } - if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, - &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key msg 2/4 with " "invalid Key Data contents"); @@ -958,6 +1103,9 @@ continue_processing: if (kde.rsn_ie) { eapol_key_ie = kde.rsn_ie; eapol_key_ie_len = kde.rsn_ie_len; + } else if (kde.osen) { + eapol_key_ie = kde.osen; + eapol_key_ie_len = kde.osen_len; } else { eapol_key_ie = kde.wpa_ie; eapol_key_ie_len = kde.wpa_ie_len; @@ -987,6 +1135,26 @@ continue_processing: return; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + if (kde.ip_addr_req && kde.ip_addr_req[0] && + wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { + int idx; + wpa_printf(MSG_DEBUG, "P2P: IP address requested in " + "EAPOL-Key exchange"); + idx = bitfield_get_first_zero(wpa_auth->ip_pool); + if (idx >= 0) { + u32 start = WPA_GET_BE32(wpa_auth->conf. + ip_addr_start); + bitfield_set(wpa_auth->ip_pool, idx); + WPA_PUT_BE32(sm->ip_addr, start + idx); + wpa_printf(MSG_DEBUG, "P2P: Assigned IP " + "address %u.%u.%u.%u to " MACSTR, + sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + } + } +#endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || @@ -1051,7 +1219,10 @@ continue_processing: sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { - if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, + data_len) && + (msg != PAIRWISE_4 || !sm->alt_snonce_valid || + wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; @@ -1080,7 +1251,7 @@ continue_processing: */ if (msg == SMK_ERROR) { #ifdef CONFIG_PEERKEY - wpa_smk_error(wpa_auth, sm, key); + wpa_smk_error(wpa_auth, sm, key_data, key_data_length); #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { @@ -1095,11 +1266,12 @@ continue_processing: wpa_request_new_ptk(sm); #ifdef CONFIG_PEERKEY } else if (msg == SMK_M1) { - wpa_smk_m1(wpa_auth, sm, key); + wpa_smk_m1(wpa_auth, sm, key, key_data, + key_data_length); #endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && - wpa_parse_kde_ies((const u8 *) (key + 1), - key_data_length, &kde) == 0 && + wpa_parse_kde_ies(key_data, key_data_length, + &kde) == 0 && kde.mac_addr) { } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, @@ -1137,7 +1309,7 @@ continue_processing: #ifdef CONFIG_PEERKEY if (msg == SMK_M3) { - wpa_smk_m3(wpa_auth, sm, key); + wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length); return; } #endif /* CONFIG_PEERKEY */ @@ -1212,17 +1384,25 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - size_t len; + struct wpa_eapol_key_192 *key192; + size_t len, mic_len, keyhdrlen; int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; int i; + u8 *key_data; + + mic_len = wpa_mic_len(sm->wpa_key_mgmt); + keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); - len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; if (force_version) version = force_version; + else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) + version = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) @@ -1230,7 +1410,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, else version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " @@ -1245,6 +1425,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1261,6 +1443,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); + key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + key_data = ((u8 *) (hdr + 1)) + keyhdrlen; key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; @@ -1286,6 +1470,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); os_memcpy(key->replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->key_replay[0].valid = TRUE; if (nonce) @@ -1295,8 +1481,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); if (kde && !encr) { - os_memcpy(key + 1, kde, kde_len); - WPA_PUT_BE16(key->key_data_length, kde_len); + os_memcpy(key_data, kde, kde_len); + if (mic_len == 24) + WPA_PUT_BE16(key192->key_data_length, kde_len); + else + WPA_PUT_BE16(key->key_data_length, kde_len); } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { @@ -1313,29 +1502,47 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { - if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, - (u8 *) (key + 1))) { + if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, + (key_data_len - 8) / 8, buf, key_data)) { os_free(hdr); os_free(buf); return; } - WPA_PUT_BE16(key->key_data_length, key_data_len); - } else { + if (mic_len == 24) + WPA_PUT_BE16(key192->key_data_length, + key_data_len); + else + WPA_PUT_BE16(key->key_data_length, + key_data_len); + } else if (sm->PTK.kek_len == 16) { u8 ek[32]; os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); os_memcpy(ek, key->key_iv, 16); - os_memcpy(ek + 16, sm->PTK.kek, 16); - os_memcpy(key + 1, buf, key_data_len); - rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); - WPA_PUT_BE16(key->key_data_length, key_data_len); + os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len); + os_memcpy(key_data, buf, key_data_len); + rc4_skip(ek, 32, 256, key_data, key_data_len); + if (mic_len == 24) + WPA_PUT_BE16(key192->key_data_length, + key_data_len); + else + WPA_PUT_BE16(key->key_data_length, + key_data_len); + } else { + os_free(hdr); + os_free(buf); + return; } os_free(buf); } if (key_info & WPA_KEY_INFO_MIC) { + u8 *key_mic; + if (!sm->PTK_valid) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " @@ -1343,8 +1550,21 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, os_free(hdr); return; } - wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, - key->key_mic); + + key_mic = key192->key_mic; /* same offset for key and key192 */ + wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, + sm->wpa_key_mgmt, version, + (u8 *) hdr, len, key_mic); +#ifdef CONFIG_TESTING_OPTIONS + if (!pairwise && + wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && + drand48() < + wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Corrupting group EAPOL-Key Key MIC"); + key_mic[0]++; + } +#endif /* CONFIG_TESTING_OPTIONS */ } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, @@ -1386,27 +1606,32 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, } -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, + size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; + struct wpa_eapol_key_192 *key192; u16 key_info; int ret = 0; - u8 mic[16]; + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + size_t mic_len = wpa_mic_len(akmp); if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); + key192 = (struct wpa_eapol_key_192 *) (hdr + 1); key_info = WPA_GET_BE16(key->key_info); - os_memcpy(mic, key->key_mic, 16); - os_memset(key->key_mic, 0, 16); - if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, - data, data_len, key->key_mic) || - os_memcmp(mic, key->key_mic, 16) != 0) + os_memcpy(mic, key192->key_mic, mic_len); + os_memset(key192->key_mic, 0, mic_len); + if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, + key_info & WPA_KEY_INFO_TYPE_MASK, + data, data_len, key192->key_mic) || + os_memcmp_const(mic, key192->key_mic, mic_len) != 0) ret = -1; - os_memcpy(key->key_mic, mic, 16); + os_memcpy(key192->key_mic, mic, mic_len); return ret; } @@ -1433,6 +1658,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) switch (event) { case WPA_AUTH: +#ifdef CONFIG_MESH + /* PTKs are derived through AMPE */ + if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) { + /* not mesh */ + break; + } + return 0; +#endif /* CONFIG_MESH */ case WPA_ASSOC: break; case WPA_DEAUTH: @@ -1596,6 +1829,7 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); wpa_group_ensure_init(sm->wpa_auth, sm->group); + sm->ReAuthenticationRequest = FALSE; /* * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat @@ -1609,12 +1843,11 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " "ANonce."); - wpa_sta_disconnect(sm->wpa_auth, sm->addr); + sm->Disconnect = TRUE; return; } wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, WPA_NONCE_LEN); - sm->ReAuthenticationRequest = FALSE; /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through @@ -1646,8 +1879,12 @@ SM_STATE(WPA_PTK, INITPMK) } #endif /* CONFIG_IEEE80211R */ } else { - wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", + sm->wpa_auth->cb.get_msk); + sm->Disconnect = TRUE; + return; } + os_memset(msk, 0, sizeof(msk)); sm->req_replay_counter_used = 0; /* IEEE 802.11i does not set keyRun to FALSE, but not doing this @@ -1666,7 +1903,7 @@ SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); - psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); #ifdef CONFIG_IEEE80211R @@ -1686,6 +1923,7 @@ SM_STATE(WPA_PTK, PTKSTART) SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; + sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { @@ -1701,16 +1939,20 @@ SM_STATE(WPA_PTK, PTKSTART) * one possible PSK for this STA. */ if (sm->wpa == WPA_VERSION_WPA2 && - wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); - if (sm->pmksa) + if (sm->pmksa) { os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); - else { + } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { + /* No KCK available to derive PMKID */ + pmkid = NULL; + } else { /* * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. @@ -1726,21 +1968,17 @@ SM_STATE(WPA_PTK, PTKSTART) } -static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk) +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, + const u8 *pmk, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); + return wpa_auth_derive_ptk_ft(sm, pmk, ptk); #endif /* CONFIG_IEEE80211R */ - wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", - sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, - (u8 *) ptk, ptk_len, - wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); - - return 0; + return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, + ptk, sm->wpa_key_mgmt, sm->pairwise); } @@ -1759,15 +1997,17 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) * the packet */ for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { - pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, + sm->p2p_dev_addr, pmk); if (pmk == NULL) break; } else pmk = sm->PMK; - wpa_derive_ptk(sm, pmk, &PTK); + wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); - if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, + sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; @@ -1789,8 +2029,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) * Verify that PMKR1Name from EAPOL-Key message 2/4 matches * with the value we derived. */ - if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name, - WPA_PMK_NAME_LEN) != 0) { + if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "PMKR1Name mismatch in FT 4-way " "handshake"); @@ -1833,7 +2073,9 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) static int ieee80211w_kde_len(struct wpa_state_machine *sm) { if (sm->mgmt_frame_prot) { - return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + size_t len; + len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); + return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len; } return 0; @@ -1844,6 +2086,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { struct wpa_igtk_kde igtk; struct wpa_group *gsm = sm->group; + u8 rsc[WPA_KEY_RSC_LEN]; + size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); if (!sm->mgmt_frame_prot) return pos; @@ -1851,19 +2095,22 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) igtk.keyid[0] = gsm->GN_igtk; igtk.keyid[1] = 0; if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); - os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + else + os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); + os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len); if (sm->wpa_auth->conf.disable_gtk) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. */ - if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) + if (random_get_bytes(igtk.igtk, len) < 0) return pos; } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, - (const u8 *) &igtk, sizeof(igtk), NULL, 0); + (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len, + NULL, 0); return pos; } @@ -1913,8 +2160,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) if (sm->wpa == WPA_VERSION_WPA && (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { - /* WPA-only STA, remove RSN IE */ + /* WPA-only STA, remove RSN IE and possible MDIE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) + wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, @@ -1968,6 +2217,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) kde_len += 300; /* FTIE + 2 * TIE */ } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) + kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; +#endif /* CONFIG_P2P */ kde = os_malloc(kde_len); if (kde == NULL) return; @@ -2029,6 +2282,16 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos += 4; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) { + u8 addr[3 * 4]; + os_memcpy(addr, sm->ip_addr, 4); + os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4); + os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4); + pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC, + addr, sizeof(addr), NULL, 0); + } +#endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | @@ -2047,7 +2310,7 @@ SM_STATE(WPA_PTK, PTKINITDONE) enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, - sm->PTK.tk1, klen)) { + sm->PTK.tk, klen)) { wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } @@ -2146,7 +2409,8 @@ SM_STEP(WPA_PTK) } break; case WPA_PTK_INITPSK: - if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, + NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, @@ -2220,7 +2484,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; struct wpa_group *gsm = sm->group; - u8 *kde, *pos, hdr[2]; + const u8 *kde; + u8 *kde_buf = NULL, *pos, hdr[2]; size_t kde_len; u8 *gtk, dummy_gtk[32]; @@ -2256,28 +2521,29 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); - kde = os_malloc(kde_len); - if (kde == NULL) + kde_buf = os_malloc(kde_len); + if (kde_buf == NULL) return; - pos = kde; + kde = pos = kde_buf; hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); + kde_len = pos - kde; } else { kde = gtk; - pos = kde + gsm->GTK_len; + kde_len = gsm->GTK_len; } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), - rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); - if (sm->wpa == WPA_VERSION_WPA2) - os_free(kde); + rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); + + os_free(kde_buf); } @@ -2354,15 +2620,16 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, #ifdef CONFIG_IEEE80211W if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { + size_t len; + len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); inc_byte_array(group->Counter, WPA_NONCE_LEN); if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", wpa_auth->addr, group->GNonce, - group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN) < 0) + group->IGTK[group->GN_igtk - 4], len) < 0) ret = -1; wpa_hexdump_key(MSG_DEBUG, "IGTK", - group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); + group->IGTK[group->GN_igtk - 4], len); } #endif /* CONFIG_IEEE80211W */ @@ -2429,7 +2696,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { - if (sm->is_wnmsleep) + if (sm == NULL || sm->is_wnmsleep) return; wpa_group_update_sta(sm, NULL); @@ -2438,7 +2705,8 @@ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) { - sm->is_wnmsleep = !!flag; + if (sm) + sm->is_wnmsleep = !!flag; } @@ -2478,26 +2746,27 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) { struct wpa_group *gsm = sm->group; u8 *start = pos; + size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); /* * IGTK subelement: * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] */ *pos++ = WNM_SLEEP_SUBELEM_IGTK; - *pos++ = 2 + 6 + WPA_IGTK_LEN; + *pos++ = 2 + 6 + len; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) return 0; pos += 6; - os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); - pos += WPA_IGTK_LEN; + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len); + pos += len; wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", gsm->GN_igtk); wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", - gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + gsm->IGTK[gsm->GN_igtk - 4], len); return pos - start; } @@ -2552,18 +2821,48 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, ret = -1; #ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && - wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, - broadcast_ether_addr, group->GN_igtk, - group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN) < 0) - ret = -1; + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { + enum wpa_alg alg; + size_t len; + + alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher); + len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); + + if (ret == 0 && + wpa_auth_set_key(wpa_auth, group->vlan_id, alg, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], len) < 0) + ret = -1; + } #endif /* CONFIG_IEEE80211W */ return ret; } +static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->group == ctx) { + wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR + " for discconnection due to fatal failure", + MAC2STR(sm->addr)); + sm->Disconnect = TRUE; + } + + return 0; +} + + +static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE"); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_FATAL_FAILURE; + wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group); +} + + static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2572,8 +2871,10 @@ static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - if (wpa_group_config_group_keys(wpa_auth, group) < 0) + if (wpa_group_config_group_keys(wpa_auth, group) < 0) { + wpa_group_fatal_failure(wpa_auth, group); return -1; + } return 0; } @@ -2584,6 +2885,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, { if (group->GInit) { wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) { + /* Do not allow group operations */ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && group->GTKAuthenticator) { wpa_group_setkeysdone(wpa_auth, group); @@ -2711,7 +3014,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2761,7 +3064,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, wpa_auth->dot11RSNA4WayHandshakeFailures); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2771,7 +3074,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", wpa_auth->group->wpa_group_state); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2813,7 +3116,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) RSN_SUITE_ARG(pairwise), sm->dot11RSNAStatsTKIPLocalMICFailures, sm->dot11RSNAStatsTKIPRemoteMICFailures); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2823,7 +3126,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) "hostapdWPAPTKGroupState=%d\n", sm->wpa_ptk_state, sm->wpa_ptk_group_state); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2907,6 +3210,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) return 0; @@ -2923,7 +3227,9 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, + NULL, 0, + wpa_auth->addr, sta_addr, session_timeout, eapol, WPA_KEY_MGMT_IEEE8021X)) return 0; @@ -2932,6 +3238,38 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, } +int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk) +{ + if (wpa_auth->conf.disable_pmksa_caching) + return -1; + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, + NULL, 0, + wpa_auth->addr, addr, 0, NULL, + WPA_KEY_MGMT_SAE)) + return 0; + + return -1; +} + + +void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) +{ + struct rsn_pmksa_cache_entry *pmksa; + + if (wpa_auth == NULL || wpa_auth->pmksa == NULL) + return; + pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); + if (pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " + MACSTR " based on request", MAC2STR(sta_addr)); + pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); + } +} + + static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { @@ -2976,6 +3314,9 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) if (sm->group == group) return 0; + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); @@ -3020,3 +3361,40 @@ int wpa_auth_uses_sae(struct wpa_state_machine *sm) return 0; return wpa_key_mgmt_sae(sm->wpa_key_mgmt); } + + +int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE; +} + + +#ifdef CONFIG_P2P +int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr) +{ + if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0) + return -1; + os_memcpy(addr, sm->ip_addr, 4); + return 0; +} +#endif /* CONFIG_P2P */ + + +int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, + struct radius_das_attrs *attr) +{ + return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr); +} + + +void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group; + + if (!wpa_auth) + return; + for (group = wpa_auth->group; group; group = group->next) + wpa_group_config_group_keys(wpa_auth, group); +} diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 465eec6a5330..2788e657435d 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -42,6 +42,7 @@ struct ft_rrb_frame { #define FT_R0KH_R1KH_PULL_DATA_LEN 44 #define FT_R0KH_R1KH_RESP_DATA_LEN 76 #define FT_R0KH_R1KH_PUSH_DATA_LEN 88 +#define FT_R0KH_R1KH_PULL_NONCE_LEN 16 struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ @@ -49,7 +50,7 @@ struct ft_r0kh_r1kh_pull_frame { le16 data_length; /* little endian length of data (44) */ u8 ap_address[ETH_ALEN]; - u8 nonce[16]; + u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; @@ -63,7 +64,7 @@ struct ft_r0kh_r1kh_resp_frame { le16 data_length; /* little endian length of data (76) */ u8 ap_address[ETH_ALEN]; - u8 nonce[16]; /* copied from pull */ + u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ u8 s1kh_id[ETH_ALEN]; /* copied from pull */ u8 pmk_r1[PMK_LEN]; @@ -142,6 +143,7 @@ struct wpa_auth_config { int tx_status; #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; + int group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R #define SSID_LEN 32 @@ -160,6 +162,15 @@ struct wpa_auth_config { #endif /* CONFIG_IEEE80211R */ int disable_gtk; int ap_mlme; +#ifdef CONFIG_TESTING_OPTIONS + double corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ +#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 */ }; typedef enum { @@ -181,7 +192,8 @@ struct wpa_auth_callbacks { void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); - const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); @@ -199,8 +211,11 @@ struct wpa_auth_callbacks { int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, - size_t tspec_ielen); + size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_MESH + int (*start_ampe)(void *ctx, const u8 *sta_addr); +#endif /* CONFIG_MESH */ }; struct wpa_authenticator * wpa_init(const u8 *addr, @@ -222,9 +237,13 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len); +int wpa_validate_osen(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *osen_ie, size_t osen_ie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr); int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm); void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); @@ -260,6 +279,10 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk); +void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); @@ -288,5 +311,13 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); int wpa_auth_uses_sae(struct wpa_state_machine *sm); +int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); + +int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr); + +struct radius_das_attrs; +int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, + struct radius_das_attrs *attr); +void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 48bf79b9b9b1..ef3249a3eb9b 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,6 +9,8 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" @@ -22,6 +24,12 @@ #ifdef CONFIG_IEEE80211R +static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, + const u8 *current_ap, const u8 *sta_addr, + u16 status, const u8 *resp_ies, + size_t resp_ies_len); + + static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { @@ -57,7 +65,7 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, u8 *tspec_ie, size_t tspec_ielen) { if (wpa_auth->cb.add_tspec == NULL) { - wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); + wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); return -1; } return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, @@ -228,8 +236,8 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, r0 = cache->pmk_r0; while (r0) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && - os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) - == 0) { + os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, + WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); if (pairwise) *pairwise = r0->pairwise; @@ -278,8 +286,8 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, r1 = cache->pmk_r1; while (r1) { if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && - os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) - == 0) { + os_memcmp_const(r1->pmk_r1_name, pmk_r1_name, + WPA_PMK_NAME_LEN) == 0) { os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); if (pairwise) *pairwise = r1->pairwise; @@ -293,22 +301,26 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, } -static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, - const u8 *s1kh_id, const u8 *r0kh_id, - size_t r0kh_id_len, const u8 *pmk_r0_name) +static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *pmk_r0_name) { struct ft_remote_r0kh *r0kh; struct ft_r0kh_r1kh_pull_frame frame, f; - r0kh = wpa_auth->conf.r0kh_list; + r0kh = sm->wpa_auth->conf.r0kh_list; while (r0kh) { - if (r0kh->id_len == r0kh_id_len && - os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) + if (r0kh->id_len == sm->r0kh_id_len && + os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == + 0) break; r0kh = r0kh->next; } - if (r0kh == NULL) + if (r0kh == NULL) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); return -1; + } wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); @@ -317,31 +329,40 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); - os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ - if (random_get_bytes(f.nonce, sizeof(f.nonce))) { + if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } + os_memcpy(sm->ft_pending_pull_nonce, f.nonce, + FT_R0KH_R1KH_PULL_NONCE_LEN); os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); - os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); + os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + if (aes_wrap(r0kh->key, sizeof(r0kh->key), + (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) return -1; - wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + wpabuf_free(sm->ft_pending_req_ies); + sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); + if (sm->ft_pending_req_ies == NULL) + return -1; + + wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); return 0; } int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk, size_t ptk_len) + struct wpa_ptk *ptk) { u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; @@ -353,7 +374,6 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; - if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " "derivation"); @@ -375,13 +395,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, sm->pairwise); - wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, sm->pmk_r1_name, - (u8 *) ptk, ptk_len, ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); - wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); - - return 0; + return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, sm->pmk_r1_name, + ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); } @@ -416,7 +432,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; - if (pad_len) { + if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; @@ -440,7 +456,8 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); subelem[4] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); - if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { + if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key, + subelem + 13)) { os_free(subelem); return NULL; } @@ -472,7 +489,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; *pos++ = WPA_IGTK_LEN; - if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { os_free(subelem); return NULL; @@ -569,8 +586,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, else { /* TSPEC accepted; include updated TSPEC in * response */ - rdie->descr_count = 1; - pos += sizeof(*tspec); + rdie->descr_count = 1; + pos += sizeof(*tspec); } return pos; } @@ -632,8 +649,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, conf = &sm->wpa_auth->conf; - if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) + if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return pos; end = pos + max_len; @@ -725,7 +741,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, ric_start = NULL; if (auth_alg == WLAN_AUTH_FT && - wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, + wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, + sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, rsnie, rsnie_len, ric_start, ric_start ? pos - ric_start : 0, @@ -769,7 +786,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) * optimized by adding the STA entry earlier. */ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, - sm->PTK.tk1, klen)) + sm->PTK.tk, klen)) return; /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ @@ -777,7 +794,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) } -static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, +static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) { @@ -787,7 +804,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; - size_t buflen, ptk_len; + size_t buflen; int ret; u8 *pos, *end; int pairwise; @@ -848,19 +865,13 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, &pairwise) < 0) { - if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, - sm->r0kh_id_len, parse.rsn_pmkid) < 0) { + if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { wpa_printf(MSG_DEBUG, "FT: Did not have matching " "PMK-R1 and unknown R0KH-ID"); return WLAN_STATUS_INVALID_PMKID; } - /* - * TODO: Should return "status pending" (and the caller should - * not send out response now). The real response will be sent - * once the response from R0KH is received. - */ - return WLAN_STATUS_INVALID_PMKID; + return -1; /* Status pending */ } wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); @@ -878,15 +889,14 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48; - wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, pmk_r1_name, - (u8 *) &sm->PTK, ptk_len, ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", - (u8 *) &sm->PTK, ptk_len); - wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, pmk_r1_name, + &sm->PTK, ptk_name, sm->wpa_key_mgmt, + pairwise) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; sm->pairwise = pairwise; + sm->PTK_valid = TRUE; wpa_ft_install_ptk(sm); buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + @@ -940,6 +950,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, u16 status; u8 *resp_ies; size_t resp_ies_len; + int res; if (sm == NULL) { wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " @@ -950,8 +961,16 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR " BSSID=" MACSTR " transaction=%d", MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); - status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, - &resp_ies_len); + sm->ft_pending_cb = cb; + sm->ft_pending_cb_ctx = ctx; + sm->ft_pending_auth_transaction = auth_transaction; + res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, + &resp_ies_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available"); + return; + } + status = res; wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR " auth_transaction=%d status=%d", @@ -969,7 +988,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; - u8 mic[16]; + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + size_t mic_len = 16; unsigned int count; if (sm == NULL) @@ -992,8 +1012,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_PMKID; } - if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) - { + if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) + != 0) { wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " "with the PMKR1Name derived from auth request"); return WLAN_STATUS_INVALID_PMKID; @@ -1039,7 +1059,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, } if (parse.r0kh_id_len != sm->r0kh_id_len || - os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + 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", @@ -1054,8 +1075,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return -1; } - if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { + if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " "ReassocReq"); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE", @@ -1066,7 +1087,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, } if (parse.rsn_pmkid == NULL || - os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + 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; @@ -1082,7 +1104,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return -1; } - if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, + if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, + sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, @@ -1092,12 +1115,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (os_memcmp(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); - wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); - wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", + ftie->mic, mic_len); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", parse.mdie - 2, parse.mdie_len + 2); wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", @@ -1166,6 +1190,8 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); + if (frame == NULL) + return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_REQUEST; frame->action_length = host_to_le16(len); @@ -1180,15 +1206,27 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) } +static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) +{ + struct wpa_state_machine *sm = ctx; + wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR, + MAC2STR(sm->addr)); + wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr, + WLAN_STATUS_SUCCESS, ies, ies_len); +} + + static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, const u8 *current_ap, const u8 *sta_addr, const u8 *body, size_t len) { struct wpa_state_machine *sm; u16 status; - u8 *resp_ies, *pos; - size_t resp_ies_len, rlen; - struct ft_rrb_frame *frame; + u8 *resp_ies; + size_t resp_ies_len; + int res; sm = wpa_ft_add_sta(wpa_auth, sta_addr); if (sm == NULL) { @@ -1199,8 +1237,33 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); - status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, - &resp_ies_len); + sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; + sm->ft_pending_cb_ctx = sm; + os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); + res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, + &resp_ies_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response"); + return 0; + } + status = res; + + res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status, + resp_ies, resp_ies_len); + os_free(resp_ies); + return res; +} + + +static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, + const u8 *current_ap, const u8 *sta_addr, + u16 status, const u8 *resp_ies, + size_t resp_ies_len) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + size_t rlen; + struct ft_rrb_frame *frame; + u8 *pos; wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR " CurrentAP=" MACSTR " status=%d", @@ -1216,6 +1279,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; frame = os_malloc(sizeof(*frame) + rlen); + if (frame == NULL) + return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_RESPONSE; frame->action_length = host_to_le16(rlen); @@ -1229,10 +1294,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, pos += ETH_ALEN; WPA_PUT_LE16(pos, status); pos += 2; - if (resp_ies) { + if (resp_ies) os_memcpy(pos, resp_ies, resp_ies_len); - os_free(resp_ies); - } wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, sizeof(*frame) + rlen); @@ -1246,7 +1309,9 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_pull_frame *frame, f; + struct ft_r0kh_r1kh_pull_frame f; + const u8 *crypt; + u8 *plain; struct ft_remote_r1kh *r1kh; struct ft_r0kh_r1kh_resp_frame resp, r; u8 pmk_r0[PMK_LEN]; @@ -1254,7 +1319,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); - if (data_len < sizeof(*frame)) + if (data_len < sizeof(f)) return -1; r1kh = wpa_auth->conf.r1kh_list; @@ -1270,11 +1335,14 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, return -1; } - frame = (struct ft_r0kh_r1kh_pull_frame *) data; + crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); + os_memset(&f, 0, sizeof(f)); + plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - frame->nonce, f.nonce) < 0) { + if (aes_unwrap(r1kh->key, sizeof(r1kh->key), + (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "request from " MACSTR, MAC2STR(src_addr)); return -1; @@ -1284,7 +1352,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, f.nonce, sizeof(f.nonce)); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", f.pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); os_memset(&resp, 0, sizeof(resp)); @@ -1311,8 +1379,10 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, WPA_PMK_NAME_LEN); r.pairwise = host_to_le16(pairwise); + os_memset(r.pad, 0, sizeof(r.pad)); - if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + if (aes_wrap(r1kh->key, sizeof(r1kh->key), + (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { os_memset(pmk_r0, 0, PMK_LEN); return -1; @@ -1326,17 +1396,64 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, } +static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + int res; + u8 *resp_ies; + size_t resp_ies_len; + u16 status; + + res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), + wpabuf_len(sm->ft_pending_req_ies), + &resp_ies, &resp_ies_len); + wpabuf_free(sm->ft_pending_req_ies); + sm->ft_pending_req_ies = NULL; + if (res < 0) + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + status = res; + wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR + " - status %u", MAC2STR(sm->addr), status); + + sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr, + sm->ft_pending_auth_transaction + 1, status, + resp_ies, resp_ies_len); + os_free(resp_ies); +} + + +static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) +{ + struct ft_r0kh_r1kh_resp_frame *frame = ctx; + + if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) + return 0; + if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, + FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) + return 0; + if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " + MACSTR " - process from timeout", MAC2STR(sm->addr)); + eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); + return 1; +} + + static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_resp_frame *frame, f; + struct ft_r0kh_r1kh_resp_frame f; + const u8 *crypt; + u8 *plain; struct ft_remote_r0kh *r0kh; - int pairwise; + int pairwise, res; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); - if (data_len < sizeof(*frame)) + if (data_len < sizeof(f)) return -1; r0kh = wpa_auth->conf.r0kh_list; @@ -1352,31 +1469,30 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, return -1; } - frame = (struct ft_r0kh_r1kh_resp_frame *) data; + crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); + os_memset(&f, 0, sizeof(f)); + plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - frame->nonce, f.nonce) < 0) { + if (aes_unwrap(r0kh->key, sizeof(r0kh->key), + (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); return -1; } - if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) - != 0) { + if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " "matching R1KH-ID"); return -1; } - /* TODO: verify that matches with a pending request - * and call this requests callback function to finish request - * processing */ - pairwise = le_to_host16(f.pairwise); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f.nonce, sizeof(f.nonce)); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" MACSTR " pairwise=0x%x", MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", @@ -1384,11 +1500,13 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", f.pmk_r1_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); + res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, + pairwise); + wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); + wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); os_memset(f.pmk_r1, 0, PMK_LEN); - return 0; + return res ? 0 : -1; } @@ -1396,7 +1514,9 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_push_frame *frame, f; + struct ft_r0kh_r1kh_push_frame f; + const u8 *crypt; + u8 *plain; struct ft_remote_r0kh *r0kh; struct os_time now; os_time_t tsend; @@ -1404,7 +1524,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - if (data_len < sizeof(*frame)) + if (data_len < sizeof(f)) return -1; r0kh = wpa_auth->conf.r0kh_list; @@ -1420,11 +1540,15 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, return -1; } - frame = (struct ft_r0kh_r1kh_push_frame *) data; + crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); + os_memset(&f, 0, sizeof(f)); + plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, + timestamp); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ - if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - frame->timestamp, f.timestamp) < 0) { + if (aes_unwrap(r0kh->key, sizeof(r0kh->key), + (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " MACSTR, MAC2STR(src_addr)); return -1; @@ -1440,8 +1564,8 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, return -1; } - if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) - != 0) { + if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " "R1KH-ID (received " MACSTR " own " MACSTR ")", MAC2STR(f.r1kh_id), @@ -1582,6 +1706,11 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, return -1; } + if (end > pos) { + wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end", + pos, end - pos); + } + return 0; } @@ -1593,6 +1722,8 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, { struct ft_r0kh_r1kh_push_frame frame, f; struct os_time now; + const u8 *plain; + u8 *crypt; os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; @@ -1614,8 +1745,14 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, os_get_time(&now); WPA_PUT_LE32(f.timestamp, now.sec); f.pairwise = host_to_le16(pairwise); - if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - f.timestamp, frame.timestamp) < 0) + os_memset(f.pad, 0, sizeof(f.pad)); + plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, + timestamp); + crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, + timestamp); + if (aes_wrap(r1kh->key, sizeof(r1kh->key), + (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + plain, crypt) < 0) return; wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 76c61ea18e06..7f8320708c39 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1,6 +1,6 @@ /* * hostapd / WPA authenticator glue code - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,11 +10,11 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" -#include "drivers/driver.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" @@ -27,6 +27,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, + struct hostapd_config *iconf, struct wpa_auth_config *wconf) { os_memset(wconf, 0, sizeof(*wconf)); @@ -48,6 +49,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; + wconf->group_mgmt_cipher = conf->group_mgmt_cipher; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R wconf->ssid_len = conf->ssid.ssid_len; @@ -72,7 +74,30 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; + if (conf->osen) { + wconf->disable_gtk = 1; + wconf->wpa = WPA_PROTO_OSEN; + wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; + wconf->wpa_pairwise = 0; + wconf->wpa_group = WPA_CIPHER_CCMP; + wconf->rsn_pairwise = WPA_CIPHER_CCMP; + wconf->rsn_preauth = 0; + wconf->disable_pmksa_caching = 1; +#ifdef CONFIG_IEEE80211W + wconf->ieee80211w = 1; +#endif /* CONFIG_IEEE80211W */ + } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_TESTING_OPTIONS + wconf->corrupt_gtk_rekey_mic_probability = + iconf->corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_P2P + os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); + os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4); + os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); + os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); +#endif /* CONFIG_P2P */ } @@ -180,11 +205,22 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, const u8 *prev_psk) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); - const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk); + const u8 *psk; + +#ifdef CONFIG_SAE + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) + return NULL; + return sta->sae->pmk; + } +#endif /* CONFIG_SAE */ + + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. @@ -213,12 +249,17 @@ static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, struct sta_info *sta; sta = ap_get_sta(hapd, addr); - if (sta == NULL) + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA"); return -1; + } key = ieee802_1x_get_key(sta->eapol_sm, &keylen); - if (key == NULL) + if (key == NULL) { + wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p", + sta->eapol_sm); return -1; + } if (keylen > *len) keylen = *len; @@ -263,6 +304,21 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, struct sta_info *sta; u32 flags = 0; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io) { + size_t hex_len = 2 * data_len + 1; + char *hex = os_malloc(hex_len); + + if (hex == NULL) + return -1; + wpa_snprintf_hex(hex, hex_len, data, data_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s", + MAC2STR(addr), hex); + os_free(hex); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + sta = ap_get_sta(hapd, addr); if (sta) flags = hostapd_sta_flags_to_drv(sta->flags); @@ -368,6 +424,21 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, struct l2_ethhdr *buf; int ret; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) { + size_t hex_len = 2 * data_len + 1; + char *hex = os_malloc(hex_len); + + if (hex == NULL) + return -1; + wpa_snprintf_hex(hex, hex_len, data, data_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s", + MAC2STR(dst), hex); + os_free(hex); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + #ifdef CONFIG_IEEE80211R if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { @@ -455,7 +526,7 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) return sta->wpa_sm; } - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL); if (sta->wpa_sm == NULL) { ap_free_sta(hapd, sta); return NULL; @@ -498,7 +569,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) const u8 *wpa_ie; size_t wpa_ie_len; - hostapd_wpa_auth_conf(hapd->conf, &_conf); + hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) _conf.tx_status = 1; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) @@ -572,7 +643,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) void hostapd_reconfig_wpa(struct hostapd_data *hapd) { struct wpa_auth_config wpa_auth_conf; - hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf); + hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf); wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); } @@ -601,5 +672,6 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd) #ifdef CONFIG_IEEE80211R l2_packet_deinit(hapd->l2); + hapd->l2 = NULL; #endif /* CONFIG_IEEE80211R */ } diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 97489d343d86..7b2cd3ea8ed4 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -26,6 +26,7 @@ struct wpa_state_machine { struct wpa_group *group; u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -57,6 +58,8 @@ struct wpa_state_machine { Boolean GUpdateStationKeys; u8 ANonce[WPA_NONCE_LEN]; u8 SNonce[WPA_NONCE_LEN]; + u8 alt_SNonce[WPA_NONCE_LEN]; + u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 PMK[PMK_LEN]; struct wpa_ptk PTK; Boolean PTK_valid; @@ -83,6 +86,7 @@ struct wpa_state_machine { unsigned int mgmt_frame_prot:1; unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; + unsigned int alt_snonce_valid:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; @@ -117,9 +121,22 @@ struct wpa_state_machine { u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key * message 2/4 */ u8 *assoc_resp_ftie; + + void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len); + void *ft_pending_cb_ctx; + struct wpabuf *ft_pending_req_ies; + u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; + u8 ft_pending_auth_transaction; + u8 ft_pending_current_ap[ETH_ALEN]; #endif /* CONFIG_IEEE80211R */ int pending_1_of_4_timeout; + +#ifdef CONFIG_P2P + u8 ip_addr[4]; +#endif /* CONFIG_P2P */ }; @@ -138,7 +155,8 @@ struct wpa_group { enum { WPA_GROUP_GTK_INIT = 0, - WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE, + WPA_GROUP_FATAL_FAILURE } wpa_group_state; u8 GMK[WPA_GMK_LEN]; @@ -148,7 +166,7 @@ struct wpa_group { Boolean first_sta_seen; Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W - u8 IGTK[2][WPA_IGTK_LEN]; + u8 IGTK[2][WPA_IGTK_MAX_LEN]; int GN_igtk, GM_igtk; #endif /* CONFIG_IEEE80211W */ }; @@ -183,6 +201,10 @@ struct wpa_authenticator { struct rsn_pmksa_cache *pmksa; struct wpa_ft_pmk_cache *ft_pmk_cache; + +#ifdef CONFIG_P2P + struct bitfield *ip_pool; +#endif /* CONFIG_P2P */ }; @@ -208,11 +230,14 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, struct wpa_stsl_negotiation *neg); void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key); + struct wpa_state_machine *sm, + const u8 *key_data, size_t key_data_len); void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key); + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len); void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key); + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len); #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R @@ -223,7 +248,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk, size_t ptk_len); + struct wpa_ptk *ptk); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 4fd0135fe6ea..f2872970affe 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -1,6 +1,6 @@ /* * hostapd - WPA/RSN IE and KDE definitions - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -200,6 +200,16 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, num_suites++; } #endif /* CONFIG_SAE */ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); + pos += RSN_SELECTOR_LEN; + num_suites++; + } #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -261,7 +271,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } /* Management Group Cipher Suite */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + switch (conf->group_mgmt_cipher) { + case WPA_CIPHER_AES_128_CMAC: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + break; + case WPA_CIPHER_BIP_GMAC_128: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128); + break; + case WPA_CIPHER_BIP_GMAC_256: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256); + break; + case WPA_CIPHER_BIP_CMAC_256: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256); + break; + default: + wpa_printf(MSG_DEBUG, + "Invalid group management cipher (0x%x)", + conf->group_mgmt_cipher); + return -1; + } pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ @@ -295,6 +323,55 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } +static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid) +{ + u8 *len; + u16 capab; + + *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 (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + capab |= WPA_CAPABILITY_MFPC; + if (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 wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) { u8 *pos, buf[128]; @@ -302,6 +379,9 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) pos = buf; + if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) { + pos = wpa_write_osen(&wpa_auth->conf, pos); + } if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { res = wpa_write_rsn_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos, NULL); @@ -407,6 +487,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (0) { } + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; #ifdef CONFIG_IEEE80211R else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_FT_802_1X; @@ -485,6 +569,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } if (0) { } + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; #ifdef CONFIG_IEEE80211R else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; @@ -534,7 +622,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, return WPA_MGMT_FRAME_PROTECTION_VIOLATION; } - if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher) + { wpa_printf(MSG_DEBUG, "Unsupported management group " "cipher %d", data.mgmt_group_cipher); return WPA_INVALID_MGMT_GROUP_CIPHER; @@ -564,12 +653,9 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } #endif /* CONFIG_IEEE80211R */ - if (ciphers & WPA_CIPHER_CCMP) - sm->pairwise = WPA_CIPHER_CCMP; - else if (ciphers & WPA_CIPHER_GCMP) - sm->pairwise = WPA_CIPHER_GCMP; - else - sm->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + if (sm->pairwise < 0) + return WPA_INVALID_PAIRWISE; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) @@ -607,7 +693,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, break; } } - if (sm->pmksa) { + if (sm->pmksa && pmkid) { wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, "PMKID found from PMKSA cache " "eap_type=%d vlan_id=%d", @@ -629,6 +715,36 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_HS20 +int wpa_validate_osen(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *osen_ie, size_t osen_ie_len) +{ + if (wpa_auth == NULL || sm == NULL) + return -1; + + /* TODO: parse OSEN element */ + sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; + sm->mgmt_frame_prot = 1; + sm->pairwise = WPA_CIPHER_CCMP; + sm->wpa = WPA_VERSION_WPA2; + + if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(osen_ie_len); + if (sm->wpa_ie == NULL) + return -1; + } + + os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len); + sm->wpa_ie_len = osen_ie_len; + + return 0; +} + +#endif /* CONFIG_HS20 */ + + /** * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs * @pos: Pointer to the IE header @@ -651,6 +767,12 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } + if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) { + ie->osen = pos; + ie->osen_len = pos[1] + 2; + 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) { @@ -711,6 +833,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #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; } diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h index 4999139510ec..d2067ba3112c 100644 --- a/src/ap/wpa_auth_ie.h +++ b/src/ap/wpa_auth_ie.h @@ -39,6 +39,13 @@ struct wpa_eapol_ie_parse { const u8 *ftie; size_t ftie_len; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ + + const u8 *osen; + size_t osen_len; }; int wpa_parse_kde_ies(const u8 *buf, size_t len, diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 5ce4f1be352e..b0e8b0bfcac3 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -40,11 +40,13 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, const u8 *ie, size_t ie_len, int ssi_signal); static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); +static void hostapd_wps_nfc_clear(struct wps_context *wps); struct wps_for_each_data { int (*func)(struct hostapd_data *h, void *ctx); void *ctx; + struct hostapd_data *calling_hapd; }; @@ -57,7 +59,14 @@ static int wps_for_each(struct hostapd_iface *iface, void *ctx) return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; - int ret = data->func(hapd, data->ctx); + int ret; + + if (hapd != data->calling_hapd && + (hapd->conf->wps_independent || + data->calling_hapd->conf->wps_independent)) + continue; + + ret = data->func(hapd, data->ctx); if (ret) return ret; } @@ -74,6 +83,7 @@ static int hostapd_wps_for_each(struct hostapd_data *hapd, struct wps_for_each_data data; data.func = func; data.ctx = ctx; + data.calling_hapd = hapd; if (iface->interfaces == NULL || iface->interfaces->for_each_interface == NULL) return wps_for_each(iface, &data); @@ -82,15 +92,24 @@ static int hostapd_wps_for_each(struct hostapd_data *hapd, } -static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, +static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { struct hostapd_data *hapd = ctx; struct hostapd_wpa_psk *p; struct hostapd_ssid *ssid = &hapd->conf->ssid; - wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " - MACSTR, MAC2STR(mac_addr)); + 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); if (psk_len != PMK_LEN) { @@ -104,8 +123,14 @@ static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, if (p == NULL) return -1; os_memcpy(p->addr, mac_addr, ETH_ALEN); + os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); os_memcpy(p->psk, psk, PMK_LEN); + if (hapd->new_psk_cb) { + hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, + psk, psk_len); + } + p->next = ssid->wpa_psk; ssid->wpa_psk = p; @@ -160,7 +185,7 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); - if (len > 0 && len < (int) sizeof(txt)) + if (!os_snprintf_error(sizeof(txt), len)) wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt); if (hapd->conf->wps_pin_requests) { @@ -263,6 +288,20 @@ static void wps_reload_config(void *eloop_data, void *user_ctx) } +void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ + /* + * Reduce race condition of the station trying to reconnect immediately + * after AP reconfiguration through WPS by rescheduling the reload + * timeout to happen after EAP completion rather than the originally + * scheduled 100 ms after new configuration became known. + */ + if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) == + 1) + wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload"); +} + + static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, size_t attr_len) { @@ -277,6 +316,84 @@ static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, } +static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, + const struct wps_credential *cred) +{ + struct hostapd_bss_config *bss = hapd->conf; + + wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); + + bss->wps_state = 2; + if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { + os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); + bss->ssid.ssid_len = cred->ssid_len; + bss->ssid.ssid_set = 1; + } + + if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && + (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) + bss->wpa = 3; + else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) + bss->wpa = 2; + else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) + bss->wpa = 1; + else + bss->wpa = 0; + + if (bss->wpa) { + if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) + bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + bss->wpa_pairwise = 0; + if (cred->encr_type & WPS_ENCR_AES) + bss->wpa_pairwise |= WPA_CIPHER_CCMP; + if (cred->encr_type & WPS_ENCR_TKIP) + bss->wpa_pairwise |= WPA_CIPHER_TKIP; + bss->rsn_pairwise = bss->wpa_pairwise; + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, + bss->wpa_pairwise, + bss->rsn_pairwise); + + if (cred->key_len >= 8 && cred->key_len < 64) { + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); + if (bss->ssid.wpa_passphrase) + os_memcpy(bss->ssid.wpa_passphrase, cred->key, + cred->key_len); + hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); + } else if (cred->key_len == 64) { + hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); + bss->ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (bss->ssid.wpa_psk && + hexstr2bin((const char *) cred->key, + bss->ssid.wpa_psk->psk, PMK_LEN) == 0) { + bss->ssid.wpa_psk->group = 1; + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = NULL; + } + } + bss->auth_algs = 1; + } else { + /* + * WPS 2.0 does not allow WEP to be configured, so no need to + * process that option here either. + */ + bss->auth_algs = 1; + } + + /* Schedule configuration reload after short period of time to allow + * EAP-WSC to be finished. + */ + eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, + NULL); + + return 0; +} + + static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) { const struct wps_credential *cred = ctx; @@ -325,6 +442,8 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) hapd->wps->ssid_len = cred->ssid_len; hapd->wps->encr_types = cred->encr_type; hapd->wps->auth_types = cred->auth_type; + hapd->wps->ap_encr_type = cred->encr_type; + hapd->wps->ap_auth_type = cred->auth_type; if (cred->key_len == 0) { os_free(hapd->wps->network_key); hapd->wps->network_key = NULL; @@ -344,7 +463,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) hapd->wps->wps_state = WPS_STATE_CONFIGURED; if (hapd->iface->config_fname == NULL) - return 0; + return hapd_wps_reconfig_in_memory(hapd, cred); len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) @@ -437,31 +556,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "auth_algs=1\n"); } else { - if ((cred->auth_type & WPS_AUTH_OPEN) && - (cred->auth_type & WPS_AUTH_SHARED)) - fprintf(nconf, "auth_algs=3\n"); - else if (cred->auth_type & WPS_AUTH_SHARED) - fprintf(nconf, "auth_algs=2\n"); - else - fprintf(nconf, "auth_algs=1\n"); - - if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) { - int key_idx = cred->key_idx; - if (key_idx) - key_idx--; - fprintf(nconf, "wep_default_key=%d\n", key_idx); - fprintf(nconf, "wep_key%d=", key_idx); - if (cred->key_len == 10 || cred->key_len == 26) { - /* WEP key as a hex string */ - for (i = 0; i < cred->key_len; i++) - fputc(cred->key[i], nconf); - } else { - /* Raw WEP key; convert to hex */ - for (i = 0; i < cred->key_len; i++) - fprintf(nconf, "%02x", cred->key[i]); - } - fprintf(nconf, "\n"); - } + /* + * WPS 2.0 does not allow WEP to be configured, so no need to + * process that option here either. + */ + fprintf(nconf, "auth_algs=1\n"); } fprintf(nconf, "# WPS configuration - END\n"); @@ -590,6 +689,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, struct wps_event_pwd_auth_fail *data) { + /* Update WPS Status - Authentication Failure */ + wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); + hapd->wps_stats.status = WPS_STATUS_FAILURE; + hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE; + os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); + hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); } @@ -617,21 +722,59 @@ static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) } -static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { - "No Error", /* WPS_EI_NO_ERROR */ - "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ - "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ -}; +static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) +{ + /* Update WPS Status - PBC Overlap */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; +} + + +static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) +{ + /* Update WPS PBC Status:PBC Timeout */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; +} + + +static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; +} + + +static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; +} + + +static void hostapd_wps_event_success(struct hostapd_data *hapd, + struct wps_event_success *success) +{ + /* Update WPS status - Success */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; + hapd->wps_stats.status = WPS_STATUS_SUCCESS; + os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); +} + static void hostapd_wps_event_fail(struct hostapd_data *hapd, struct wps_event_fail *fail) { + /* Update WPS status - Failure */ + hapd->wps_stats.status = WPS_STATUS_FAILURE; + os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); + + hapd->wps_stats.failure_reason = fail->error_indication; + if (fail->error_indication > 0 && fail->error_indication < NUM_WPS_EI_VALUES) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, - wps_event_fail_reason[fail->error_indication]); + wps_ei_str(fail->error_indication)); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", @@ -653,17 +796,28 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event, hostapd_wps_event_fail(hapd, &data->fail); break; case WPS_EV_SUCCESS: + hostapd_wps_event_success(hapd, &data->success); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); break; case WPS_EV_PWD_AUTH_FAIL: hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); break; case WPS_EV_PBC_OVERLAP: + hostapd_wps_event_pbc_overlap(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); break; case WPS_EV_PBC_TIMEOUT: + hostapd_wps_event_pbc_timeout(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); break; + case WPS_EV_PBC_ACTIVE: + hostapd_wps_event_pbc_active(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + hostapd_wps_event_pbc_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); + break; case WPS_EV_ER_AP_ADD: break; case WPS_EV_ER_AP_REMOVE: @@ -685,7 +839,16 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event, } -static void hostapd_wps_clear_ies(struct hostapd_data *hapd) +static int hostapd_wps_rf_band_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + + return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ +} + + +static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) { wpabuf_free(hapd->wps_beacon_ie); hapd->wps_beacon_ie = NULL; @@ -693,6 +856,9 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd) wpabuf_free(hapd->wps_probe_resp_ie); hapd->wps_probe_resp_ie = NULL; + if (deinit_only) + return; + hostapd_set_ap_wps_ie(hapd); } @@ -706,7 +872,8 @@ static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) return 0; for (j = 0; j < iface->num_bss; j++) { struct hostapd_data *hapd = iface->bss[j]; - if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) { + if (hapd->wps && !hapd->conf->wps_independent && + !is_nil_uuid(hapd->wps->uuid)) { *uuid = hapd->wps->uuid; return 1; } @@ -774,6 +941,21 @@ static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, } +static void hostapd_free_wps(struct wps_context *wps) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(wps->dev.vendor_ext[i]); + wps_device_data_free(&wps->dev); + os_free(wps->network_key); + hostapd_wps_nfc_clear(wps); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + os_free(wps); +} + + int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf) { @@ -781,7 +963,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, struct wps_registrar_config cfg; if (conf->wps_state == 0) { - hostapd_wps_clear_ies(hapd); + hostapd_wps_clear_ies(hapd, 0); return 0; } @@ -791,6 +973,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->cred_cb = hostapd_wps_cred_cb; wps->event_cb = hostapd_wps_event_cb; + wps->rf_band_cb = hostapd_wps_rf_band_cb; wps->cb_ctx = hapd; os_memset(&cfg, 0, sizeof(cfg)); @@ -799,7 +982,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (is_nil_uuid(hapd->conf->uuid)) { const u8 *uuid; uuid = get_own_uuid(hapd->iface); - if (uuid) { + if (uuid && !conf->wps_independent) { os_memcpy(wps->uuid, uuid, UUID_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " "interface", wps->uuid, UUID_LEN); @@ -829,7 +1012,6 @@ int hostapd_init_wps(struct hostapd_data *hapd, os_strdup(hapd->conf->serial_number) : NULL; wps->config_methods = wps_config_methods_str2bin(hapd->conf->config_methods); -#ifdef CONFIG_WPS2 if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { @@ -844,14 +1026,11 @@ int hostapd_init_wps(struct hostapd_data *hapd, "virtual_push_button for WPS 2.0 compliance"); wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; } -#endif /* CONFIG_WPS2 */ os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, WPS_DEV_TYPE_LEN); - if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) { - os_free(wps); - return -1; - } + if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) + goto fail; wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); @@ -869,7 +1048,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & WPA_CIPHER_CCMP) + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) wps->encr_types |= WPS_ENCR_AES; if (conf->rsn_pairwise & WPA_CIPHER_TKIP) wps->encr_types |= WPS_ENCR_TKIP; @@ -890,18 +1069,6 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { wps->encr_types |= WPS_ENCR_NONE; wps->auth_types |= WPS_AUTH_OPEN; - } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) { - wps->encr_types |= WPS_ENCR_WEP; - if (conf->auth_algs & WPA_AUTH_ALG_OPEN) - wps->auth_types |= WPS_AUTH_OPEN; - if (conf->auth_algs & WPA_AUTH_ALG_SHARED) - wps->auth_types |= WPS_AUTH_SHARED; - } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) { - wps->auth_types |= WPS_AUTH_OPEN; - if (conf->default_wep_key_len) - wps->encr_types |= WPS_ENCR_WEP; - else - wps->encr_types |= WPS_ENCR_NONE; } if (conf->ssid.wpa_psk_file) { @@ -911,19 +1078,15 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); } else if (conf->ssid.wpa_psk) { wps->network_key = os_malloc(2 * PMK_LEN + 1); - if (wps->network_key == NULL) { - os_free(wps); - return -1; - } + if (wps->network_key == NULL) + goto fail; wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, conf->ssid.wpa_psk->psk, PMK_LEN); wps->network_key_len = 2 * PMK_LEN; } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { wps->network_key = os_malloc(conf->ssid.wep.len[0]); - if (wps->network_key == NULL) { - os_free(wps); - return -1; - } + if (wps->network_key == NULL) + goto fail; os_memcpy(wps->network_key, conf->ssid.wep.key[0], conf->ssid.wep.len[0]); wps->network_key_len = conf->ssid.wep.len[0]; @@ -934,6 +1097,8 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->psk_set = 1; } + wps->ap_auth_type = wps->auth_types; + wps->ap_encr_type = wps->encr_types; if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { /* Override parameters to enable security by default */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; @@ -962,13 +1127,12 @@ int hostapd_init_wps(struct hostapd_data *hapd, cfg.dualband = 1; if (cfg.dualband) wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); + cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk; wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); - os_free(wps->network_key); - os_free(wps); - return -1; + goto fail; } #ifdef CONFIG_WPS_UPNP @@ -984,6 +1148,10 @@ int hostapd_init_wps(struct hostapd_data *hapd, hapd->wps = wps; return 0; + +fail: + hostapd_free_wps(wps); + return -1; } @@ -998,8 +1166,7 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd) if (hostapd_wps_upnp_init(hapd, wps) < 0) { wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); wps_registrar_deinit(wps->registrar); - os_free(wps->network_key); - os_free(wps); + hostapd_free_wps(wps); hapd->wps = NULL; return -1; } @@ -1012,6 +1179,7 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd) static void hostapd_wps_nfc_clear(struct wps_context *wps) { #ifdef CONFIG_WPS_NFC + wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps); wps->ap_nfc_dev_pw_id = 0; wpabuf_free(wps->ap_nfc_dh_pubkey); wps->ap_nfc_dh_pubkey = NULL; @@ -1027,21 +1195,19 @@ void hostapd_deinit_wps(struct hostapd_data *hapd) { eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); - if (hapd->wps == NULL) + eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL); + if (hapd->wps == NULL) { + hostapd_wps_clear_ies(hapd, 1); return; + } #ifdef CONFIG_WPS_UPNP hostapd_wps_upnp_deinit(hapd); #endif /* CONFIG_WPS_UPNP */ wps_registrar_deinit(hapd->wps->registrar); - os_free(hapd->wps->network_key); - wps_device_data_free(&hapd->wps->dev); - wpabuf_free(hapd->wps->dh_pubkey); - wpabuf_free(hapd->wps->dh_privkey); wps_free_pending_msgs(hapd->wps->upnp_msgs); - hostapd_wps_nfc_clear(hapd->wps); - os_free(hapd->wps); + hostapd_free_wps(hapd->wps); hapd->wps = NULL; - hostapd_wps_clear_ies(hapd); + hostapd_wps_clear_ies(hapd, 1); } @@ -1123,7 +1289,7 @@ static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) { const u8 *p2p_dev_addr = ctx; if (hapd->wps == NULL) - return 0; + return -1; return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); } @@ -1139,7 +1305,7 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd, static int wps_cancel(struct hostapd_data *hapd, void *ctx) { if (hapd->wps == NULL) - return 0; + return -1; wps_registrar_wps_cancel(hapd->wps->registrar); ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); @@ -1260,6 +1426,16 @@ static int hostapd_rx_req_put_wlan_response( return 0; } + if (!sta->eapol_sm) { + /* + * This can happen, e.g., if an ER sends an extra message after + * the station has disassociated (but not fully + * deauthenticated). + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized"); + return 0; + } + p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; @@ -1406,7 +1582,7 @@ int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int ret; ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); - if (ret < 0 || ret >= (int) sizeof(data.pin_txt)) + if (os_snprintf_error(sizeof(data.pin_txt), ret)) return -1; data.timeout = timeout; return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); @@ -1453,8 +1629,6 @@ int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, if (encr) { if (os_strncmp(encr, "NONE", 4) == 0) cred.encr_type = WPS_ENCR_NONE; - else if (os_strncmp(encr, "WEP", 3) == 0) - cred.encr_type = WPS_ENCR_WEP; else if (os_strncmp(encr, "TKIP", 4) == 0) cred.encr_type = WPS_ENCR_TKIP; else if (os_strncmp(encr, "CCMP", 4) == 0) @@ -1569,7 +1743,8 @@ struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, if (hapd->wps == NULL) return NULL; - ret = wps_get_oob_cred(hapd->wps); + ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd), + hapd->iconf->channel); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); @@ -1583,8 +1758,150 @@ struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, } +struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef) +{ + struct wpabuf *ret; + + if (hapd->wps == NULL) + return NULL; + + if (hapd->conf->wps_nfc_dh_pubkey == NULL) { + struct wps_context *wps = hapd->wps; + if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey, + &hapd->conf->wps_nfc_dh_privkey) < 0) + return NULL; + hostapd_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + wps->ap_nfc_dh_pubkey = + wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = + wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { + hostapd_wps_nfc_clear(wps); + return NULL; + } + } + + ret = wps_build_nfc_handover_sel(hapd->wps, + hapd->conf->wps_nfc_dh_pubkey, + hapd->own_addr, hapd->iface->freq); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, + 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(hapd->wps->registrar, + attr.oob_dev_password, + DEV_PW_NFC_CONNECTION_HANDOVER, + NULL, 0, 1); + +out: + wpabuf_free(wps); + return ret; +} + + struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) { + if (hapd->conf->wps_nfc_pw_from_config) { + return wps_nfc_token_build(ndef, + hapd->conf->wps_nfc_dev_pw_id, + hapd->conf->wps_nfc_dh_pubkey, + hapd->conf->wps_nfc_dev_pw); + } + return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, &hapd->conf->wps_nfc_dh_pubkey, &hapd->conf->wps_nfc_dh_privkey, @@ -1595,6 +1912,7 @@ struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) { struct wps_context *wps = hapd->wps; + struct wpabuf *pw; if (wps == NULL) return -1; @@ -1606,10 +1924,22 @@ int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) return -1; hostapd_wps_nfc_clear(wps); + wpa_printf(MSG_DEBUG, + "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)", + hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps); wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); - wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw); + pw = hapd->conf->wps_nfc_dev_pw; + wps->ap_nfc_dev_pw = wpabuf_alloc( + wpabuf_len(pw) * 2 + 1); + if (wps->ap_nfc_dev_pw) { + wpa_snprintf_hex_uppercase( + (char *) wpabuf_put(wps->ap_nfc_dev_pw, + wpabuf_len(pw) * 2), + wpabuf_len(pw) * 2 + 1, + wpabuf_head(pw), wpabuf_len(pw)); + } if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || !wps->ap_nfc_dev_pw) { @@ -1623,6 +1953,8 @@ int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) { + wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s", + hapd->conf->iface); hostapd_wps_nfc_clear(hapd->wps); } diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h index 4e5026b45b6c..204bd820a547 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -16,6 +16,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, int hostapd_init_wps_complete(struct hostapd_data *hapd); void hostapd_deinit_wps(struct hostapd_data *hapd); void hostapd_update_wps(struct hostapd_data *hapd); +void hostapd_wps_eap_completed(struct hostapd_data *hapd); int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, const char *uuid, const char *pin, int timeout); int hostapd_wps_button_pushed(struct hostapd_data *hapd, @@ -35,6 +36,10 @@ int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, const struct wpabuf *data); struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, int ndef); +struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef); +int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, + const struct wpabuf *req, + const struct wpabuf *sel); struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef); int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd); void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd); @@ -60,6 +65,10 @@ static inline void hostapd_update_wps(struct hostapd_data *hapd) { } +static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ +} + static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t buflen) diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c new file mode 100644 index 000000000000..8f77015ef57f --- /dev/null +++ b/src/ap/x_snoop.c @@ -0,0 +1,123 @@ +/* + * Generic Snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * 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 "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "x_snoop.h" + + +int x_snoop_init(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + + if (!conf->isolate) { + wpa_printf(MSG_DEBUG, + "x_snoop: ap_isolate must be enabled for x_snoop"); + return -1; + } + + if (conf->bridge[0] == '\0') { + wpa_printf(MSG_DEBUG, + "x_snoop: Bridge must be configured for x_snoop"); + return -1; + } + + if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, + 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable hairpin_mode on the bridge port"); + return -1; + } + + if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable proxyarp on the bridge port"); + return -1; + } + + if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, + 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable accepting gratuitous ARP on the bridge"); + return -1; + } + + return 0; +} + + +struct l2_packet_data * +x_snoop_get_l2_packet(struct hostapd_data *hapd, + void (*handler)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + enum l2_packet_filter_type type) +{ + struct hostapd_bss_config *conf = hapd->conf; + struct l2_packet_data *l2; + + l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1); + if (l2 == NULL) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to initialize L2 packet processing %s", + strerror(errno)); + return NULL; + } + + if (l2_packet_set_packet_filter(l2, type)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to set L2 packet filter for type: %d", + type); + l2_packet_deinit(l2); + return NULL; + } + + return l2; +} + + +void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd, + struct sta_info *sta, u8 *buf, + size_t len) +{ + int res; + u8 addr[ETH_ALEN]; + u8 *dst_addr = buf; + + if (!(dst_addr[0] & 0x01)) + return; + + wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion " + MACSTR " -> " MACSTR " (len %u)", + MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len); + + /* save the multicast destination address for restoring it later */ + os_memcpy(addr, buf, ETH_ALEN); + + os_memcpy(buf, sta->addr, ETH_ALEN); + res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to send mcast to ucast converted packet to " + MACSTR, MAC2STR(sta->addr)); + } + + /* restore the multicast destination address */ + os_memcpy(buf, addr, ETH_ALEN); +} + + +void x_snoop_deinit(struct hostapd_data *hapd) +{ + hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0); + hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0); + hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0); +} diff --git a/src/ap/x_snoop.h b/src/ap/x_snoop.h new file mode 100644 index 000000000000..e43a78d0d83d --- /dev/null +++ b/src/ap/x_snoop.h @@ -0,0 +1,56 @@ +/* + * Generic Snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef X_SNOOP_H +#define X_SNOOP_H + +#include "l2_packet/l2_packet.h" + +#ifdef CONFIG_PROXYARP + +int x_snoop_init(struct hostapd_data *hapd); +struct l2_packet_data * +x_snoop_get_l2_packet(struct hostapd_data *hapd, + void (*handler)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + enum l2_packet_filter_type type); +void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd, + struct sta_info *sta, u8 *buf, + size_t len); +void x_snoop_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_PROXYARP */ + +static inline int x_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline struct l2_packet_data * +x_snoop_get_l2_packet(struct hostapd_data *hapd, + void (*handler)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + enum l2_packet_filter_type type) +{ + return NULL; +} + +static inline void +x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd, + struct sta_info *sta, void *buf, + size_t len) +{ +} + +static inline void x_snoop_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_PROXYARP */ + +#endif /* X_SNOOP_H */ diff --git a/src/common/Makefile b/src/common/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c new file mode 100644 index 000000000000..56b11220c9ed --- /dev/null +++ b/src/common/common_module_tests.c @@ -0,0 +1,172 @@ +/* + * common module tests + * Copyright (c) 2014, Jouni Malinen + * + * 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 "ieee802_11_common.h" +#include "wpa_common.h" + + +struct ieee802_11_parse_test_data { + u8 *data; + size_t len; + ParseRes result; + int count; +}; + +static const struct ieee802_11_parse_test_data parse_tests[] = { + { (u8 *) "", 0, ParseOK, 0 }, + { (u8 *) " ", 1, ParseFailed, 0 }, + { (u8 *) "\xff\x00", 2, ParseUnknown, 1 }, + { (u8 *) "\xff\x01", 2, ParseFailed, 0 }, + { (u8 *) "\xdd\x03\x01\x02\x03", 5, ParseUnknown, 1 }, + { (u8 *) "\xdd\x04\x01\x02\x03\x04", 6, ParseUnknown, 1 }, + { (u8 *) "\xdd\x04\x00\x50\xf2\x02", 6, ParseUnknown, 1 }, + { (u8 *) "\xdd\x05\x00\x50\xf2\x02\x02", 7, ParseOK, 1 }, + { (u8 *) "\xdd\x05\x00\x50\xf2\x02\xff", 7, ParseUnknown, 1 }, + { (u8 *) "\xdd\x04\x00\x50\xf2\xff", 6, ParseUnknown, 1 }, + { (u8 *) "\xdd\x04\x50\x6f\x9a\xff", 6, ParseUnknown, 1 }, + { (u8 *) "\xdd\x04\x00\x90\x4c\x33", 6, ParseOK, 1 }, + { (u8 *) "\xdd\x04\x00\x90\x4c\xff\xdd\x04\x00\x90\x4c\x33", 12, + ParseUnknown, 2 }, + { (u8 *) "\x10\x01\x00\x21\x00", 5, ParseOK, 2 }, + { (u8 *) "\x24\x00", 2, ParseOK, 1 }, + { (u8 *) "\x38\x00", 2, ParseOK, 1 }, + { (u8 *) "\x54\x00", 2, ParseOK, 1 }, + { (u8 *) "\x5a\x00", 2, ParseOK, 1 }, + { (u8 *) "\x65\x00", 2, ParseOK, 1 }, + { (u8 *) "\x65\x12\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11", + 20, ParseOK, 1 }, + { (u8 *) "\x6e\x00", 2, ParseOK, 1 }, + { (u8 *) "\xc7\x00", 2, ParseOK, 1 }, + { (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 }, + { NULL, 0, ParseOK, 0 } +}; + +static int ieee802_11_parse_tests(void) +{ + int i, ret = 0; + + wpa_printf(MSG_INFO, "ieee802_11_parse tests"); + + for (i = 0; parse_tests[i].data; i++) { + const struct ieee802_11_parse_test_data *test; + struct ieee802_11_elems elems; + ParseRes res; + + test = &parse_tests[i]; + res = ieee802_11_parse_elems(test->data, test->len, &elems, 1); + if (res != test->result || + ieee802_11_ie_count(test->data, test->len) != test->count) { + wpa_printf(MSG_ERROR, "ieee802_11_parse test %d failed", + i); + ret = -1; + } + } + + if (ieee802_11_vendor_ie_concat((const u8 *) "\x00\x01", 2, 0) != NULL) + { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test failed"); + ret = -1; + } + + return ret; +} + + +struct rsn_ie_parse_test_data { + u8 *data; + size_t len; + int result; +}; + +static const struct rsn_ie_parse_test_data rsn_parse_tests[] = { + { (u8 *) "", 0, -1 }, + { (u8 *) "\x30\x00", 2, -1 }, + { (u8 *) "\x30\x02\x01\x00", 4, 0 }, + { (u8 *) "\x30\x02\x00\x00", 4, -2 }, + { (u8 *) "\x30\x02\x02\x00", 4, -2 }, + { (u8 *) "\x30\x02\x00\x01", 4, -2 }, + { (u8 *) "\x30\x02\x00\x00\x00", 5, -2 }, + { (u8 *) "\x30\x03\x01\x00\x00", 5, -3 }, + { (u8 *) "\x30\x06\x01\x00\x00\x00\x00\x00", 8, -1 }, + { (u8 *) "\x30\x06\x01\x00\x00\x0f\xac\x04", 8, 0 }, + { (u8 *) "\x30\x07\x01\x00\x00\x0f\xac\x04\x00", 9, -5 }, + { (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x00", 10, -4 }, + { (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x01", 10, -4 }, + { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04", + 14, 0 }, + { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x00\x01\x00\x0f\xac\x04", + 14, -4 }, + { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x06", + 14, -1 }, + { (u8 *) "\x30\x10\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x04\x00\x0f\xac\x08", + 18, 0 }, + { (u8 *) "\x30\x0d\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00", + 15, -7 }, + { (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x00", + 16, -6 }, + { (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x01", + 16, -6 }, + { (u8 *) "\x30\x12\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01", + 20, 0 }, + { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x01\x00\x0f\xac\x02", + 24, 0 }, + { (u8 *) "\x30\x13\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00", + 21, 0 }, + { (u8 *) "\x30\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00", + 22, 0 }, + { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00", + 24, 0 }, + { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x01", + 24, -9 }, + { (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x00\x00\x00", + 28, -10 }, + { (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06", + 28, 0 }, + { (u8 *) "\x30\x1c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06\x01\x02", + 30, 0 }, + { NULL, 0, 0 } +}; + +static int rsn_ie_parse_tests(void) +{ + int i, ret = 0; + + wpa_printf(MSG_INFO, "rsn_ie_parse tests"); + + for (i = 0; rsn_parse_tests[i].data; i++) { + const struct rsn_ie_parse_test_data *test; + struct wpa_ie_data data; + + test = &rsn_parse_tests[i]; + if (wpa_parse_wpa_ie_rsn(test->data, test->len, &data) != + test->result) { + wpa_printf(MSG_ERROR, "rsn_ie_parse test %d failed", i); + ret = -1; + } + } + + return ret; +} + + +int common_module_tests(void) +{ + int ret = 0; + + wpa_printf(MSG_INFO, "common module tests"); + + if (ieee802_11_parse_tests() < 0 || + rsn_ie_parse_tests() < 0) + ret = -1; + + return ret; +} diff --git a/src/common/defs.h b/src/common/defs.h index 281dd8a5eb5c..b5f4f801eda2 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Common definitions - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,11 +23,15 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_CIPHER_WEP104 BIT(2) #define WPA_CIPHER_TKIP BIT(3) #define WPA_CIPHER_CCMP BIT(4) -#ifdef CONFIG_IEEE80211W #define WPA_CIPHER_AES_128_CMAC BIT(5) -#endif /* CONFIG_IEEE80211W */ #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) @@ -44,13 +48,19 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #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_IEEE8021X_SHA256)); + 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) @@ -58,7 +68,8 @@ 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_SAE | + WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_ft(int akm) @@ -77,13 +88,27 @@ static inline int wpa_key_mgmt_sae(int akm) static inline int wpa_key_mgmt_sha256(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_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_wpa_psk(akm) || + wpa_key_mgmt_sae(akm); } static inline int wpa_key_mgmt_wpa_any(int akm) @@ -100,6 +125,7 @@ static inline int wpa_key_mgmt_cckm(int akm) #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) @@ -117,41 +143,12 @@ enum wpa_alg { WPA_ALG_PMK, WPA_ALG_GCMP, WPA_ALG_SMS4, - WPA_ALG_KRK -}; - -/** - * enum wpa_cipher - Cipher suites - */ -enum wpa_cipher { - CIPHER_NONE, - CIPHER_WEP40, - CIPHER_TKIP, - CIPHER_CCMP, - CIPHER_WEP104, - CIPHER_GCMP, - CIPHER_SMS4 -}; - -/** - * enum wpa_key_mgmt - Key management suites - */ -enum wpa_key_mgmt { - KEY_MGMT_802_1X, - KEY_MGMT_PSK, - KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, - KEY_MGMT_WPA_NONE, - KEY_MGMT_FT_802_1X, - KEY_MGMT_FT_PSK, - KEY_MGMT_802_1X_SHA256, - KEY_MGMT_PSK_SHA256, - KEY_MGMT_WPS, - KEY_MGMT_SAE, - KEY_MGMT_FT_SAE, - KEY_MGMT_WAPI_PSK, - KEY_MGMT_WAPI_CERT, - KEY_MGMT_CCKM + 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 }; /** @@ -312,10 +309,21 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_PIN, WPA_CTRL_REQ_EAP_OTP, WPA_CTRL_REQ_EAP_PASSPHRASE, + WPA_CTRL_REQ_SIM, 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, +}; + #endif /* DEFS_H */ diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h index 4811f38aab36..6958661f78b5 100644 --- a/src/common/eapol_common.h +++ b/src/common/eapol_common.h @@ -22,17 +22,28 @@ struct ieee802_1x_hdr { /* 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_ENCAPSULATED_ASF_ALERT = 4, + IEEE802_1X_TYPE_EAPOL_MKA = 5, }; enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c new file mode 100644 index 000000000000..e8babb52a9c5 --- /dev/null +++ b/src/common/hw_features_common.c @@ -0,0 +1,438 @@ +/* + * Common hostapd/wpa_supplicant HW features + * Copyright (c) 2002-2013, Jouni Malinen + * 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, 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 && + elems.ht_operation_len >= sizeof(*oper)) { + 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) + return 0; + + if (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; +} + + +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 && + elems.ht_operation_len >= sizeof(*oper)) { + 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) + return 0; + + if (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 && + elems.ht_capabilities_len >= + sizeof(struct ieee80211_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) +{ + int tmp; + + 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) + return -1; + if (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) + return -1; + if (vht_oper_chwidth == 3 && !center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (30 + freq - 5000 - center_segment0 * 5) / 20; + tmp /= 2; + if (data->center_freq1 != 5000 + + center_segment0 * 5 - 20 + 40 * tmp) + return -1; + data->center_freq1 = 5000 + center_segment0 * 5; + 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; + /* primary 40 part must match the HT configuration */ + tmp = (70 + freq - 5000 - center_segment0 * 5) / 20; + tmp /= 2; + if (data->center_freq1 != 5000 + + center_segment0 * 5 - 60 + 40 * tmp) + return -1; + data->center_freq1 = 5000 + center_segment0 * 5; + break; + } + + return 0; +} diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h new file mode 100644 index 000000000000..7f43d00c5b27 --- /dev/null +++ b/src/common/hw_features_common.h @@ -0,0 +1,40 @@ +/* + * Common hostapd/wpa_supplicant HW features + * Copyright (c) 2002-2013, Jouni Malinen + * 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_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end); +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/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 98fadda1f90c..aca0b73223bb 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "defs.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -107,10 +108,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, 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)\n", + "(type=%d len=%lu)", pos[3], (unsigned long) elen); return -1; } @@ -122,6 +128,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, 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 " @@ -188,25 +203,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->supp_rates = pos; elems->supp_rates_len = elen; break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; case WLAN_EID_DS_PARAMS: elems->ds_params = pos; elems->ds_params_len = elen; break; case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; case WLAN_EID_TIM: - elems->tim = pos; - elems->tim_len = elen; - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; break; case WLAN_EID_CHALLENGE: elems->challenge = pos; @@ -231,8 +233,6 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->rsn_ie_len = elen; break; case WLAN_EID_PWR_CAPABILITY: - elems->power_cap = pos; - elems->power_cap_len = elen; break; case WLAN_EID_SUPPORTED_CHANNELS: elems->supp_channels = pos; @@ -258,6 +258,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->ht_operation = pos; elems->ht_operation_len = elen; 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: elems->vht_capabilities = pos; elems->vht_capabilities_len = elen; @@ -266,6 +278,11 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->vht_operation = pos; elems->vht_operation_len = elen; 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; @@ -275,6 +292,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, 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; @@ -288,6 +311,16 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, 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; default: unknown++; if (!show_errors) @@ -486,3 +519,405 @@ int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], return 0; } + + +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) +{ + enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; + + if (freq >= 2412 && freq <= 2472) { + mode = HOSTAPD_MODE_IEEE80211G; + *channel = (freq - 2407) / 5; + } else if (freq == 2484) { + mode = HOSTAPD_MODE_IEEE80211B; + *channel = 14; + } else if (freq >= 4900 && freq < 5000) { + mode = HOSTAPD_MODE_IEEE80211A; + *channel = (freq - 4000) / 5; + } else if (freq >= 5000 && freq < 5900) { + mode = HOSTAPD_MODE_IEEE80211A; + *channel = (freq - 5000) / 5; + } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + mode = HOSTAPD_MODE_IEEE80211AD; + *channel = (freq - 56160) / 2160; + } + + return mode; +} + + +static const char *us_op_class_cc[] = { + "US", "CA", NULL +}; + +static const char *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 *jp_op_class_cc[] = { + "JP", NULL +}; + +static const char *cn_op_class_cc[] = { + "CN", "CA", NULL +}; + + +static int country_match(const char *cc[], const char *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 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 125: /* channels 149,153,157,161,165,169 */ + 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 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 +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 55fa49d1f6da..7f0b296d2b00 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -13,11 +13,7 @@ struct ieee802_11_elems { const u8 *ssid; const u8 *supp_rates; - const u8 *fh_params; const u8 *ds_params; - const u8 *cf_params; - const u8 *tim; - const u8 *ibss_params; const u8 *challenge; const u8 *erp_info; const u8 *ext_supp_rates; @@ -26,32 +22,36 @@ struct ieee802_11_elems { const u8 *wmm; /* WMM Information or Parameter Element */ const u8 *wmm_tspec; const u8 *wps_ie; - const u8 *power_cap; 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; u8 ssid_len; u8 supp_rates_len; - u8 fh_params_len; u8 ds_params_len; - u8 cf_params_len; - u8 tim_len; - u8 ibss_params_len; u8 challenge_len; u8 erp_info_len; u8 ext_supp_rates_len; @@ -60,22 +60,29 @@ struct ieee802_11_elems { u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ u8 wmm_tspec_len; u8 wps_ie_len; - u8 power_cap_len; u8 supp_channels_len; u8 mdie_len; u8 ftie_len; u8 timeout_int_len; u8 ht_capabilities_len; u8 ht_operation_len; + u8 mesh_config_len; + u8 mesh_id_len; + u8 peer_mgmt_len; u8 vht_capabilities_len; u8 vht_operation_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; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -99,5 +106,11 @@ struct hostapd_wmm_ac_params { 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); +int ieee80211_is_dfs(int freq); +int supp_rates_11b_only(struct ieee802_11_elems *elems); + +const char * fc2str(u16 fc); #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index e873545f6fa1..2e51935b8e3b 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1,6 +1,6 @@ /* * IEEE 802.11 Frame type definitions - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * Copyright (c) 2007-2008 Intel Corporation * * This software may be distributed under the terms of the BSD license. @@ -25,6 +25,8 @@ #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) @@ -161,6 +163,8 @@ #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_QUERY_RESP_OUTSTANDING 95 +#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 @@ -192,6 +196,16 @@ #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 */ @@ -203,6 +217,7 @@ #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 @@ -218,16 +233,20 @@ /* 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 @@ -241,8 +260,14 @@ #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_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 @@ -266,9 +291,11 @@ #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_VENDOR_SPECIFIC 127 @@ -281,6 +308,19 @@ #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 @@ -300,6 +340,19 @@ #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 @@ -548,6 +601,18 @@ struct ieee80211_mgmt { * Entries (optional) */ u8 variable[0]; } STRUCT_PACKED bss_tm_resp; + struct { + u8 action; /* 6 */ + u8 dialog_token; + u8 query_reason; + /* BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_query; + struct { + u8 action; /* 15 */ + u8 variable[0]; + } STRUCT_PACKED slf_prot_action; } u; } STRUCT_PACKED action; } u; @@ -557,9 +622,12 @@ struct ieee80211_mgmt { /* 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; + 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; @@ -567,18 +635,36 @@ struct ieee80211_ht_capabilities { } STRUCT_PACKED; +/* HT Operation element */ struct ieee80211_ht_operation { - u8 control_chan; - u8 ht_param; - le16 operation_mode; - le16 stbc_param; - u8 basic_set[16]; + 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; - u8 vht_supported_mcs_set[8]; + struct { + le16 rx_map; + le16 rx_highest; + le16 tx_map; + le16 tx_highest; + } vht_supported_mcs_set; } STRUCT_PACKED; struct ieee80211_vht_operation { @@ -588,6 +674,15 @@ struct ieee80211_vht_operation { 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 */ @@ -596,7 +691,9 @@ struct ieee80211_vht_operation { #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))) @@ -614,73 +711,86 @@ struct ieee80211_vht_operation { #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)) -#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13)) +/* 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_SUPPORTED ((u16) BIT(10)) +#define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10)) #define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) - - -#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0)) -#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) -#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) -#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3)) -#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4)) -#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5)) -#define TX_BEAMFORM_CAP_CALIB_OFFSET 6 -#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) -#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9)) -#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10)) -#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 -#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 -#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 -#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17 -#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 -#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 -#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 -#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 - - -#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0)) -#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) -#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) -#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) -#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) -#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) -#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) - +/* 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_REC_TRANS_CHNL_WIDTH ((u8) BIT(2)) +#define HT_INFO_HT_PARAM_STA_CHNL_WIDTH ((u8) BIT(2)) #define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) -#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) -#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) - - -#define OP_MODE_PURE 0 -#define OP_MODE_MAY_BE_LEGACY_STAS 1 -#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 -#define OP_MODE_MIXED 3 - -#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ - (0x0001 | 0x0002) -#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 -#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) -#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) -#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4)) - -#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6)) -#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7)) -#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8)) -#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9)) -#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) -#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) +/* 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 @@ -688,8 +798,11 @@ struct ieee80211_vht_operation { /* 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)) @@ -698,29 +811,62 @@ struct ieee80211_vht_operation { #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_BEAMFORMER_ANTENNAS_MAX ((u32) BIT(13) | BIT(14)) -#define VHT_CAP_SOUNDING_DIMENTION_MAX ((u32) BIT(16) | BIT(17)) +#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 ((u32) BIT(23)) + +#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 @@ -759,6 +905,8 @@ struct wmm_information_element { } 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 @@ -826,16 +974,18 @@ struct wmm_tspec_element { /* Access Categories / ACI to AC coding */ -enum { +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_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 @@ -843,6 +993,21 @@ enum { #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) */ @@ -868,6 +1033,15 @@ enum p2p_attr_id { 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 }; @@ -889,6 +1063,7 @@ enum p2p_attr_id { #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) /* Invitation Flags */ #define P2P_INVITATION_FLAGS_TYPE BIT(0) @@ -911,6 +1086,13 @@ enum p2p_status_code { 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-" @@ -943,6 +1125,7 @@ enum p2p_service_protocol_type { P2P_SERV_UPNP = 2, P2P_SERV_WS_DISCOVERY = 3, P2P_SERV_WIFI_DISPLAY = 4, + P2P_SERV_P2PS = 11, P2P_SERV_VENDOR_SPECIFIC = 255 }; @@ -967,8 +1150,24 @@ enum wifi_display_subelem { 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 */ @@ -982,6 +1181,11 @@ enum wifi_display_subelem { #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 @@ -993,7 +1197,14 @@ enum wifi_display_subelem { /* 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 */ @@ -1035,6 +1246,37 @@ enum wnm_action { #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) @@ -1082,4 +1324,34 @@ enum wnm_sleep_mode_subelement_id { 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; + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h new file mode 100644 index 000000000000..cc88caa8d2f3 --- /dev/null +++ b/src/common/ieee802_1x_defs.h @@ -0,0 +1,78 @@ +/* + * IEEE Std 802.1X-2010 definitions + * 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 IEEE802_1X_DEFS_H +#define IEEE802_1X_DEFS_H + +#define CS_ID_LEN 8 +#define CS_ID_GCM_AES_128 {0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01} +#define CS_NAME_GCM_AES_128 "GCM-AES-128" + +enum macsec_policy { + /** + * Should secure sessions. + * This accepts key server's advice to determine whether to secure the + * session or not. + */ + SHOULD_SECURE, + + /** + * Disabled MACsec - do not secure sessions. + */ + DO_NOT_SECURE, +}; + + +/* IEEE Std 802.1X-2010 - Table 11-6 - MACsec Capability */ +enum macsec_cap { + /** + * MACsec is not implemented + */ + MACSEC_CAP_NOT_IMPLEMENTED, + + /** + * 'Integrity without confidentiality' + */ + MACSEC_CAP_INTEGRITY, + + /** + * 'Integrity without confidentiality' and + * 'Integrity and confidentiality' with a confidentiality offset of 0 + */ + MACSEC_CAP_INTEG_AND_CONF, + + /** + * 'Integrity without confidentiality' and + * 'Integrity and confidentiality' with a confidentiality offset of 0, + * 30, 50 + */ + MACSEC_CAP_INTEG_AND_CONF_0_30_50, +}; + +enum validate_frames { + Disabled, + Checked, + Strict, +}; + +/* IEEE Std 802.1X-2010 - Table 11-6 - Confidentiality Offset */ +enum confidentiality_offset { + CONFIDENTIALITY_NONE = 0, + CONFIDENTIALITY_OFFSET_0 = 1, + CONFIDENTIALITY_OFFSET_30 = 2, + CONFIDENTIALITY_OFFSET_50 = 3, +}; + +/* IEEE Std 802.1X-2010 - Table 9-2 */ +#define DEFAULT_PRIO_INFRA_PORT 0x10 +#define DEFAULT_PRIO_PRIMRAY_AP 0x30 +#define DEFAULT_PRIO_SECONDARY_AP 0x50 +#define DEFAULT_PRIO_GROUP_CA_MEMBER 0x70 +#define DEFAULT_PRIO_NOT_KEY_SERVER 0xFF + +#endif /* IEEE802_1X_DEFS_H */ diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h index 858b51d388c0..4dc34c4ad322 100644 --- a/src/common/privsep_commands.h +++ b/src/common/privsep_commands.h @@ -31,7 +31,9 @@ struct privsep_cmd_associate u8 bssid[ETH_ALEN]; u8 ssid[32]; size_t ssid_len; + int hwmode; int freq; + int channel; int pairwise_suite; int group_suite; int key_mgmt_suite; diff --git a/src/common/qca-vendor-attr.h b/src/common/qca-vendor-attr.h new file mode 100644 index 000000000000..6f51803e9c41 --- /dev/null +++ b/src/common/qca-vendor-attr.h @@ -0,0 +1,28 @@ +/* + * Qualcomm Atheros vendor specific attribute 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 QCA_VENDOR_ATTR_H +#define QCA_VENDOR_ATTR_H + +/* + * This file defines some of the attributes used with Qualcomm Atheros OUI + * 00:13:74 in a way that is not suitable for qca-vendor.h, e.g., due to + * compiler dependencies. + */ + +struct qca_avoid_freq_range { + u32 start_freq; + u32 end_freq; +} __attribute__ ((packed)); + +struct qca_avoid_freq_list { + u32 count; + struct qca_avoid_freq_range range[0]; +} __attribute__ ((packed)); + +#endif /* QCA_VENDOR_ATTR_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h new file mode 100644 index 000000000000..2117ee7028c1 --- /dev/null +++ b/src/common/qca-vendor.h @@ -0,0 +1,246 @@ +/* + * 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 for QCA */ + 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, +}; + + +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, + /* 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, + /* 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, +}; + +/** + * 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. + * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits + */ +enum qca_wlan_vendor_features { + QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, + 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 +}; +#endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c new file mode 100644 index 000000000000..588895808fde --- /dev/null +++ b/src/common/sae.c @@ -0,0 +1,1069 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * 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 "crypto/dh_groups.h" +#include "ieee802_11_defs.h" +#include "sae.h" + + +int sae_set_group(struct sae_data *sae, int group) +{ + struct sae_temporary_data *tmp; + + sae_clear_data(sae); + tmp = sae->tmp = os_zalloc(sizeof(*tmp)); + if (tmp == NULL) + return -1; + + /* First, check if this is an ECC group */ + tmp->ec = crypto_ec_init(group); + if (tmp->ec) { + sae->group = group; + tmp->prime_len = crypto_ec_prime_len(tmp->ec); + tmp->prime = crypto_ec_get_prime(tmp->ec); + tmp->order = crypto_ec_get_order(tmp->ec); + return 0; + } + + /* Not an ECC group, check FFC */ + tmp->dh = dh_groups_get(group); + if (tmp->dh) { + sae->group = group; + tmp->prime_len = tmp->dh->prime_len; + if (tmp->prime_len > SAE_MAX_PRIME_LEN) { + sae_clear_data(sae); + return -1; + } + + tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, + tmp->prime_len); + if (tmp->prime_buf == NULL) { + sae_clear_data(sae); + return -1; + } + tmp->prime = tmp->prime_buf; + + tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, + tmp->dh->order_len); + if (tmp->order_buf == NULL) { + sae_clear_data(sae); + return -1; + } + tmp->order = tmp->order_buf; + + return 0; + } + + /* Unsupported group */ + return -1; +} + + +void sae_clear_temp_data(struct sae_data *sae) +{ + struct sae_temporary_data *tmp; + if (sae == NULL || sae->tmp == NULL) + return; + tmp = sae->tmp; + crypto_ec_deinit(tmp->ec); + crypto_bignum_deinit(tmp->prime_buf, 0); + crypto_bignum_deinit(tmp->order_buf, 0); + crypto_bignum_deinit(tmp->sae_rand, 1); + crypto_bignum_deinit(tmp->pwe_ffc, 1); + crypto_bignum_deinit(tmp->own_commit_scalar, 0); + crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); + crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); + crypto_ec_point_deinit(tmp->pwe_ecc, 1); + crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); + crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); + wpabuf_free(tmp->anti_clogging_token); + bin_clear_free(tmp, sizeof(*tmp)); + sae->tmp = NULL; +} + + +void sae_clear_data(struct sae_data *sae) +{ + if (sae == NULL) + return; + sae_clear_temp_data(sae); + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + os_memset(sae, 0, sizeof(*sae)); +} + + +static void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + +static struct crypto_bignum * sae_get_rand(struct sae_data *sae) +{ + u8 val[SAE_MAX_PRIME_LEN]; + int iter = 0; + struct crypto_bignum *bn = NULL; + int order_len_bits = crypto_bignum_bits(sae->tmp->order); + size_t order_len = (order_len_bits + 7) / 8; + + if (order_len > sizeof(val)) + return NULL; + + for (;;) { + if (iter++ > 100) + return NULL; + if (random_get_bytes(val, order_len) < 0) + return NULL; + if (order_len_bits % 8) + buf_shift_right(val, order_len, 8 - order_len_bits % 8); + bn = crypto_bignum_init_set(val, order_len); + if (bn == NULL) + return NULL; + if (crypto_bignum_is_zero(bn) || + crypto_bignum_is_one(bn) || + crypto_bignum_cmp(bn, sae->tmp->order) >= 0) { + crypto_bignum_deinit(bn, 0); + continue; + } + break; + } + + os_memset(val, 0, order_len); + return bn; +} + + +static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) +{ + crypto_bignum_deinit(sae->tmp->sae_rand, 1); + sae->tmp->sae_rand = sae_get_rand(sae); + if (sae->tmp->sae_rand == NULL) + return NULL; + return sae_get_rand(sae); +} + + +static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) +{ + wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR + " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { + os_memcpy(key, addr1, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); + } else { + os_memcpy(key, addr2, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); + } +} + + +static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, + struct crypto_ec_point *pwe) +{ + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *x; + int y_bit; + size_t bits; + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + prime, sae->tmp->prime_len, pwd_value, bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", + pwd_value, sae->tmp->prime_len); + + if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) + return 0; + + y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + + x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (x == NULL) + return -1; + if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { + crypto_bignum_deinit(x, 0); + wpa_printf(MSG_DEBUG, "SAE: No solution found"); + return 0; + } + crypto_bignum_deinit(x, 0); + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + + return 1; +} + + +static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, + struct crypto_bignum *pwe) +{ + u8 pwd_value[SAE_MAX_PRIME_LEN]; + size_t bits = sae->tmp->prime_len * 8; + u8 exp[1]; + struct crypto_bignum *a, *b; + int res; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, + bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, + sae->tmp->prime_len); + + if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) + { + wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); + return 0; + } + + /* PWE = pwd-value^((p-1)/r) modulo p */ + + a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + + if (sae->tmp->dh->safe_prime) { + /* + * r = (p-1)/2 for the group used here, so this becomes: + * PWE = pwd-value^2 modulo p + */ + exp[0] = 2; + b = crypto_bignum_init_set(exp, sizeof(exp)); + } else { + /* Calculate exponent: (p-1)/r */ + exp[0] = 1; + b = crypto_bignum_init_set(exp, sizeof(exp)); + if (b == NULL || + crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || + crypto_bignum_div(b, sae->tmp->order, b) < 0) { + crypto_bignum_deinit(b, 0); + b = NULL; + } + } + + if (a == NULL || b == NULL) + res = -1; + else + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + + crypto_bignum_deinit(a, 0); + crypto_bignum_deinit(b, 0); + + if (res < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); + return -1; + } + + /* if (PWE > 1) --> found */ + if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { + wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); + return 0; + } + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + return 1; +} + + +static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len) +{ + u8 counter, k = 4; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[2]; + size_t len[2]; + int found = 0; + struct crypto_ec_point *pwe_tmp; + + if (sae->tmp->pwe_ecc == NULL) { + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (sae->tmp->pwe_ecc == NULL) + return -1; + } + pwe_tmp = crypto_ec_point_init(sae->tmp->ec); + if (pwe_tmp == NULL) + return -1; + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + addr[1] = &counter; + len[1] = sizeof(counter); + + /* + * Continue for at least k iterations to protect against side-channel + * attacks that attempt to determine the number of iterations required + * in the loop. + */ + for (counter = 1; counter < k || !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, + pwd_seed) < 0) + break; + res = sae_test_pwd_seed_ecc(sae, pwd_seed, + found ? pwe_tmp : + sae->tmp->pwe_ecc); + if (res < 0) + break; + if (res == 0) + continue; + if (found) { + wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " + "already selected)"); + } else { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + crypto_ec_point_deinit(pwe_tmp, 1); + + return found ? 0 : -1; +} + + +static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len) +{ + u8 counter; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[2]; + size_t len[2]; + int found = 0; + + if (sae->tmp->pwe_ffc == NULL) { + sae->tmp->pwe_ffc = crypto_bignum_init(); + if (sae->tmp->pwe_ffc == NULL) + return -1; + } + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + addr[1] = &counter; + len[1] = sizeof(counter); + + for (counter = 1; !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, + pwd_seed) < 0) + break; + res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); + if (res < 0) + break; + if (res > 0) { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + return found ? 0 : -1; +} + + +static int sae_derive_commit_element_ecc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ecc) { + sae->tmp->own_commit_element_ecc = + crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->own_commit_element_ecc) + return -1; + } + + if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, + sae->tmp->own_commit_element_ecc) < 0 || + crypto_ec_point_invert(sae->tmp->ec, + sae->tmp->own_commit_element_ecc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return -1; + } + + return 0; +} + + +static int sae_derive_commit_element_ffc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ffc) { + sae->tmp->own_commit_element_ffc = crypto_bignum_init(); + if (!sae->tmp->own_commit_element_ffc) + return -1; + } + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0 || + crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, + sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return -1; + } + + return 0; +} + + +static int sae_derive_commit(struct sae_data *sae) +{ + struct crypto_bignum *mask; + int ret = -1; + + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return -1; + } + + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + + if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) + goto fail; + if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) + goto fail; + + ret = 0; +fail: + crypto_bignum_deinit(mask, 1); + return ret; +} + + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + struct sae_data *sae) +{ + if (sae->tmp == NULL) + return -1; + if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len) < 0) + return -1; + if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len) < 0) + return -1; + if (sae_derive_commit(sae) < 0) + return -1; + return 0; +} + + +static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) +{ + struct crypto_ec_point *K; + int ret = -1; + + K = crypto_ec_point_init(sae->tmp->ec); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (point-at-infinity), reject + * k = F(K) (= x coordinate) + */ + + if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, + sae->peer_commit_scalar, K) < 0 || + crypto_ec_point_add(sae->tmp->ec, K, + sae->tmp->peer_commit_element_ecc, K) < 0 || + crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || + crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || + crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_ec_point_deinit(K, 1); + return ret; +} + + +static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) +{ + struct crypto_bignum *K; + int ret = -1; + + K = crypto_bignum_init(); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (one), reject. + * k = F(K) (= x coordinate) + */ + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, + sae->tmp->prime, K) < 0 || + crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, + sae->tmp->prime, K) < 0 || + crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 + || + crypto_bignum_is_one(K) || + crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < + 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_bignum_deinit(K, 1); + return ret; +} + + +static int sae_derive_keys(struct sae_data *sae, const u8 *k) +{ + u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; + u8 keyseed[SHA256_MAC_LEN]; + u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; + struct crypto_bignum *tmp; + int ret = -1; + + tmp = crypto_bignum_init(); + if (tmp == NULL) + goto fail; + + /* keyseed = H(<0>32, k) + * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", + * (commit-scalar + peer-commit-scalar) modulo r) + * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) + */ + + os_memset(null_key, 0, sizeof(null_key)); + hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, + keyseed); + wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); + + crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, + tmp); + crypto_bignum_mod(tmp, sae->tmp->order, tmp); + crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); + sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", + val, sae->tmp->prime_len, keys, sizeof(keys)); + os_memset(keyseed, 0, sizeof(keyseed)); + os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); + os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memset(keys, 0, sizeof(keys)); + wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); + + ret = 0; +fail: + crypto_bignum_deinit(tmp, 0); + return ret; +} + + +int sae_process_commit(struct sae_data *sae) +{ + u8 k[SAE_MAX_PRIME_LEN]; + if (sae->tmp == NULL || + (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || + (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || + sae_derive_keys(sae, k) < 0) + return -1; + return 0; +} + + +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token) +{ + u8 *pos; + + if (sae->tmp == NULL) + return; + + wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ + if (token) { + wpabuf_put_buf(buf, token); + wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", + wpabuf_head(token), wpabuf_len(token)); + } + pos = wpabuf_put(buf, sae->tmp->prime_len); + crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, + sae->tmp->prime_len, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", + pos, sae->tmp->prime_len); + if (sae->tmp->ec) { + pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); + crypto_ec_point_to_bin(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + pos, pos + sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + } else { + pos = wpabuf_put(buf, sae->tmp->prime_len); + crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, + sae->tmp->prime_len, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", + pos, sae->tmp->prime_len); + } +} + + +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) +{ + if (allowed_groups) { + int i; + for (i = 0; allowed_groups[i] > 0; i++) { + if (allowed_groups[i] == group) + break; + } + if (allowed_groups[i] != group) { + wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " + "enabled in the current configuration", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + } + + if (sae->state == SAE_COMMITTED && group != sae->group) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (group != sae->group && sae_set_group(sae, group) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (sae->tmp == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (sae->tmp->dh && !allowed_groups) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " + "explicit configuration enabling it", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + return WLAN_STATUS_SUCCESS; +} + + +static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, + const u8 *end, const u8 **token, + size_t *token_len) +{ + if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { + size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * + sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); + if (token) + *token = *pos; + if (token_len) + *token_len = tlen; + *pos += tlen; + } else { + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + } +} + + +static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + struct crypto_bignum *peer_scalar; + + if (*pos + sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); + if (peer_scalar == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* + * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for + * the peer and it is in Authenticated state, the new Commit Message + * shall be dropped if the peer-scalar is identical to the one used in + * the existing protocol instance. + */ + if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && + crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { + wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " + "peer-commit-scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* 0 < scalar < r */ + if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + sae->peer_commit_scalar = peer_scalar; + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", + *pos, sae->tmp->prime_len); + *pos += sae->tmp->prime_len; + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + + if (pos + 2 * sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* element x and y coordinates < p */ + if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(pos + sae->tmp->prime_len, prime, + sae->tmp->prime_len) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " + "element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + + crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); + sae->tmp->peer_commit_element_ecc = + crypto_ec_point_from_bin(sae->tmp->ec, pos); + if (sae->tmp->peer_commit_element_ecc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (!crypto_ec_point_is_on_curve(sae->tmp->ec, + sae->tmp->peer_commit_element_ecc)) { + wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + struct crypto_bignum *res; + + if (pos + sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, + sae->tmp->prime_len); + + crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); + sae->tmp->peer_commit_element_ffc = + crypto_bignum_init_set(pos, sae->tmp->prime_len); + if (sae->tmp->peer_commit_element_ffc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, + sae->tmp->prime) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* scalar-op(r, ELEMENT) = 1 modulo p */ + res = crypto_bignum_init(); + if (res == NULL || + crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + sae->tmp->order, sae->tmp->prime, res) < 0 || + !crypto_bignum_is_one(res)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); + crypto_bignum_deinit(res, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + crypto_bignum_deinit(res, 0); + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + if (sae->tmp->dh) + return sae_parse_commit_element_ffc(sae, pos, end); + return sae_parse_commit_element_ecc(sae, pos, end); +} + + +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups) +{ + const u8 *pos = data, *end = data + len; + u16 res; + + /* Check Finite Cyclic Group */ + if (pos + 2 > end) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); + if (res != WLAN_STATUS_SUCCESS) + return res; + pos += 2; + + /* Optional Anti-Clogging Token */ + sae_parse_commit_token(sae, &pos, end, token, token_len); + + /* commit-scalar */ + res = sae_parse_commit_scalar(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* commit-element */ + return sae_parse_commit_element(sae, pos, end); +} + + +static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const u8 *element1, size_t element1_len, + const struct crypto_bignum *scalar2, + const u8 *element2, size_t element2_len, + u8 *confirm) +{ + const u8 *addr[5]; + size_t len[5]; + u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; + + /* Confirm + * CN(key, X, Y, Z, ...) = + * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) + * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, + * peer-commit-scalar, PEER-COMMIT-ELEMENT) + * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, + * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) + */ + addr[0] = sc; + len[0] = 2; + crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), + sae->tmp->prime_len); + addr[1] = scalar_b1; + len[1] = sae->tmp->prime_len; + addr[2] = element1; + len[2] = element1_len; + crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), + sae->tmp->prime_len); + addr[3] = scalar_b2; + len[3] = sae->tmp->prime_len; + addr[4] = element2; + len[4] = element2_len; + hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, + confirm); +} + + +static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_ec_point *element1, + const struct crypto_bignum *scalar2, + const struct crypto_ec_point *element2, + u8 *confirm) +{ + u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; + u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; + + crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, + element_b1 + sae->tmp->prime_len); + crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, + element_b2 + sae->tmp->prime_len); + + sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, + scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); +} + + +static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_bignum *element1, + const struct crypto_bignum *scalar2, + const struct crypto_bignum *element2, + u8 *confirm) +{ + u8 element_b1[SAE_MAX_PRIME_LEN]; + u8 element_b2[SAE_MAX_PRIME_LEN]; + + crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), + sae->tmp->prime_len); + crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), + sae->tmp->prime_len); + + sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, + scalar2, element_b2, sae->tmp->prime_len, confirm); +} + + +void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) +{ + const u8 *sc; + + if (sae->tmp == NULL) + return; + + /* Send-Confirm */ + sc = wpabuf_put(buf, 0); + wpabuf_put_le16(buf, sae->send_confirm); + sae->send_confirm++; + + if (sae->tmp->ec) + sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + wpabuf_put(buf, SHA256_MAC_LEN)); + else + sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + wpabuf_put(buf, SHA256_MAC_LEN)); +} + + +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) +{ + u8 verifier[SHA256_MAC_LEN]; + + if (len < 2 + SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); + + if (sae->tmp == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); + return -1; + } + + if (sae->tmp->ec) + sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + verifier); + else + sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + verifier); + + if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); + wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", + data + 2, SHA256_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", + verifier, SHA256_MAC_LEN); + return -1; + } + + return 0; +} diff --git a/src/common/sae.h b/src/common/sae.h new file mode 100644 index 000000000000..3ebf40cf4a45 --- /dev/null +++ b/src/common/sae.h @@ -0,0 +1,67 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * 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) + +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/src/common/tnc.h b/src/common/tnc.h new file mode 100644 index 000000000000..108acf90fea1 --- /dev/null +++ b/src/common/tnc.h @@ -0,0 +1,121 @@ +/* + * TNC - Common defines + * Copyright (c) 2007-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TNC_H +#define TNC_H + +typedef unsigned long TNC_UInt32; +typedef unsigned char *TNC_BufferReference; + +typedef TNC_UInt32 TNC_IMVID; +typedef TNC_UInt32 TNC_IMCID; +typedef TNC_UInt32 TNC_ConnectionID; +typedef TNC_UInt32 TNC_ConnectionState; +typedef TNC_UInt32 TNC_RetryReason; +typedef TNC_UInt32 TNC_IMV_Action_Recommendation; +typedef TNC_UInt32 TNC_IMV_Evaluation_Result; +typedef TNC_UInt32 TNC_MessageType; +typedef TNC_MessageType *TNC_MessageTypeList; +typedef TNC_UInt32 TNC_VendorID; +typedef TNC_UInt32 TNC_Subtype; +typedef TNC_UInt32 TNC_MessageSubtype; +typedef TNC_UInt32 TNC_Version; +typedef TNC_UInt32 TNC_Result; +typedef TNC_UInt32 TNC_AttributeID; + +typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)( + TNC_IMVID imvID, + char *functionName, + void **pOutfunctionPointer); +typedef TNC_Result (*TNC_TNCS_ReportMessageTypesPointer)( + TNC_IMVID imvID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount); +typedef TNC_Result (*TNC_TNCS_SendMessagePointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType); +typedef TNC_Result (*TNC_TNCS_RequestHandshakeRetryPointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason); +typedef TNC_Result (*TNC_TNCS_ProvideRecommendationPointer)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_IMV_Action_Recommendation recommendation, + TNC_IMV_Evaluation_Result evaluation); +typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( + TNC_IMCID imcID, + char *functionName, + void **pOutfunctionPointer); +typedef TNC_Result (*TNC_TNCC_SendMessagePointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType); +typedef TNC_Result (*TNC_TNCC_ReportMessageTypesPointer)( + TNC_IMCID imcID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount); +typedef TNC_Result (*TNC_TNCC_RequestHandshakeRetryPointer)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason); + +#define TNC_IFIMV_VERSION_1 1 +#define TNC_IFIMC_VERSION_1 1 + +#define TNC_RESULT_SUCCESS 0 +#define TNC_RESULT_NOT_INITIALIZED 1 +#define TNC_RESULT_ALREADY_INITIALIZED 2 +#define TNC_RESULT_NO_COMMON_VERSION 3 +#define TNC_RESULT_CANT_RETRY 4 +#define TNC_RESULT_WONT_RETRY 5 +#define TNC_RESULT_INVALID_PARAMETER 6 +#define TNC_RESULT_CANT_RESPOND 7 +#define TNC_RESULT_ILLEGAL_OPERATION 8 +#define TNC_RESULT_OTHER 9 +#define TNC_RESULT_FATAL 10 + +#define TNC_CONNECTION_STATE_CREATE 0 +#define TNC_CONNECTION_STATE_HANDSHAKE 1 +#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 +#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 +#define TNC_CONNECTION_STATE_ACCESS_NONE 4 +#define TNC_CONNECTION_STATE_DELETE 5 + +#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) +#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff) + +/* TNCC-TNCS Message Types */ +#define TNC_TNCCS_RECOMMENDATION 0x00000001 +#define TNC_TNCCS_ERROR 0x00000002 +#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 +#define TNC_TNCCS_REASONSTRINGS 0x00000004 + +/* Possible TNC_IMV_Action_Recommendation values: */ +enum IMV_Action_Recommendation { + TNC_IMV_ACTION_RECOMMENDATION_ALLOW, + TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS, + TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION +}; + +/* Possible TNC_IMV_Evaluation_Result values: */ +enum IMV_Evaluation_Result { + TNC_IMV_EVALUATION_RESULT_COMPLIANT, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR, + TNC_IMV_EVALUATION_RESULT_ERROR, + TNC_IMV_EVALUATION_RESULT_DONT_KNOW +}; + +#endif /* TNC_H */ diff --git a/src/common/version.h b/src/common/version.h index 04ed0acb6238..e39a8dbf92e7 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.0" VERSION_STR_POSTFIX +#define VERSION_STR "2.4" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 8d7a11cfc74b..de81d53694c2 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1,6 +1,6 @@ /* * WPA/RSN - Shared functions for supplicant and authenticator - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,6 +12,7 @@ #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" @@ -19,9 +20,35 @@ #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) @@ -37,18 +64,18 @@ * 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, int ver, const u8 *buf, size_t len, - u8 *mic) +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[SHA1_MAC_LEN]; + u8 hash[SHA384_MAC_LEN]; switch (ver) { #ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: - return hmac_md5(key, 16, buf, len, mic); + return hmac_md5(key, key_len, buf, len, mic); #endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: - if (hmac_sha1(key, 16, buf, len, hash)) + if (hmac_sha1(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, MD5_MAC_LEN); break; @@ -56,6 +83,30 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, 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; } @@ -74,8 +125,9 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, * @nonce1: ANonce or SNonce * @nonce2: SNonce or ANonce * @ptk: Buffer for pairwise transient key - * @ptk_len: Length of PTK - * @use_sha256: Whether to use SHA256-based KDF + * @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", @@ -86,12 +138,14 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || * Min(INonce, PNonce) || Max(INonce, PNonce)) */ -void 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, - u8 *ptk, size_t ptk_len, int use_sha256) +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); @@ -111,27 +165,44 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, 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_IEEE80211W - if (use_sha256) + if (wpa_key_mgmt_sha256(akmp)) sha256_prf(pmk, pmk_len, label, data, sizeof(data), - ptk, ptk_len); + tmp, ptk_len); else #endif /* CONFIG_IEEE80211W */ - sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk, - ptk_len); + 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", ptk, ptk_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, const u8 *sta_addr, const u8 *ap_addr, - u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, +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) @@ -139,6 +210,12 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, u8 *buf, *pos; size_t buf_len; + if (kck_len != 16) { + wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", + (unsigned int) kck_len); + return -1; + } + buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; buf = os_malloc(buf_len); if (buf == NULL) @@ -335,7 +412,6 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, #endif /* CONFIG_IEEE80211R */ -#ifndef CONFIG_NO_WPA2 static int rsn_selector_to_bitfield(const u8 *s) { if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) @@ -354,6 +430,18 @@ static int rsn_selector_to_bitfield(const u8 *s) #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; } @@ -382,9 +470,32 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) 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; return 0; } -#endif /* CONFIG_NO_WPA2 */ + + +static int wpa_cipher_valid_group(int cipher) +{ + return wpa_cipher_valid_pairwise(cipher) || + cipher == WPA_CIPHER_WEP104 || + cipher == WPA_CIPHER_WEP40 || + 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 */ /** @@ -397,7 +508,6 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { -#ifndef CONFIG_NO_WPA2 const struct rsn_ie_hdr *hdr; const u8 *pos; int left; @@ -443,13 +553,11 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); -#ifdef CONFIG_IEEE80211W - if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) { - wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group " - "cipher", __func__); + if (!wpa_cipher_valid_group(data->group_cipher)) { + wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x", + __func__, data->group_cipher); return -1; } -#endif /* CONFIG_IEEE80211W */ pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } else if (left > 0) { @@ -463,7 +571,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) { + 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; @@ -491,7 +599,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) { + 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; @@ -514,17 +622,17 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } if (left >= 2) { - data->num_pmkid = WPA_GET_LE16(pos); + u16 num_pmkid = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (left < (int) data->num_pmkid * PMKID_LEN) { + if (num_pmkid > (unsigned int) left / PMKID_LEN) { wpa_printf(MSG_DEBUG, "%s: PMKID underflow " - "(num_pmkid=%lu left=%d)", - __func__, (unsigned long) data->num_pmkid, - left); + "(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; @@ -534,7 +642,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, #ifdef CONFIG_IEEE80211W if (left >= 4) { data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); - if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + 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); @@ -546,14 +654,12 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, #endif /* CONFIG_IEEE80211W */ if (left > 0) { - wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", - __func__, left); + wpa_hexdump(MSG_DEBUG, + "wpa_parse_wpa_ie_rsn: ignore trailing bytes", + pos, left); } return 0; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -643,7 +749,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { + 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; @@ -664,7 +770,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { + 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; @@ -687,8 +793,9 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, } if (left > 0) { - wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", - __func__, left); + wpa_hexdump(MSG_DEBUG, + "wpa_parse_wpa_ie_wpa: ignore trailing bytes", + pos, left); } return 0; @@ -812,15 +919,17 @@ void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, * * IEEE Std 802.11r-2008 - 8.5.1.5.5 */ -void 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, - u8 *ptk, size_t ptk_len, u8 *ptk_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) { 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 || @@ -836,7 +945,12 @@ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; - sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_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; + + sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len); /* * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || @@ -857,6 +971,19 @@ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, 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 */ @@ -896,6 +1023,72 @@ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, } +#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) @@ -918,6 +1111,12 @@ const char * wpa_cipher_txt(int cipher) 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"; } @@ -959,12 +1158,52 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) 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) @@ -1084,8 +1323,15 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) 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; @@ -1102,6 +1348,8 @@ int wpa_cipher_key_len(int cipher) 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: @@ -1118,6 +1366,10 @@ int wpa_cipher_rsc_len(int cipher) 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: @@ -1127,6 +1379,14 @@ int wpa_cipher_to_alg(int cipher) case WPA_CIPHER_WEP104: case WPA_CIPHER_WEP40: return WPA_ALG_WEP; + 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; } @@ -1134,7 +1394,9 @@ int wpa_cipher_to_alg(int cipher) int wpa_cipher_valid_pairwise(int cipher) { - return cipher == WPA_CIPHER_CCMP || + return cipher == WPA_CIPHER_CCMP_256 || + cipher == WPA_CIPHER_GCMP_256 || + cipher == WPA_CIPHER_CCMP || cipher == WPA_CIPHER_GCMP || cipher == WPA_CIPHER_TKIP; } @@ -1142,6 +1404,10 @@ int wpa_cipher_valid_pairwise(int cipher) 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); @@ -1159,58 +1425,252 @@ u32 wpa_cipher_to_suite(int proto, int cipher) 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 *pos, int ciphers) +int rsn_cipher_put_suites(u8 *start, int ciphers) { - int num_suites = 0; + 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; - num_suites++; } if (ciphers & WPA_CIPHER_GCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); pos += RSN_SELECTOR_LEN; - num_suites++; } if (ciphers & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; - num_suites++; } if (ciphers & WPA_CIPHER_NONE) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); pos += RSN_SELECTOR_LEN; - num_suites++; } - return num_suites; + return (pos - start) / RSN_SELECTOR_LEN; } -int wpa_cipher_put_suites(u8 *pos, int ciphers) +int wpa_cipher_put_suites(u8 *start, int ciphers) { - int num_suites = 0; + u8 *pos = start; if (ciphers & WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); pos += WPA_SELECTOR_LEN; - num_suites++; } if (ciphers & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); pos += WPA_SELECTOR_LEN; - num_suites++; } if (ciphers & WPA_CIPHER_NONE) { RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); pos += WPA_SELECTOR_LEN; - num_suites++; } - return num_suites; + 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; + if (ciphers & WPA_CIPHER_WEP104) + return WPA_CIPHER_WEP104; + if (ciphers & WPA_CIPHER_WEP40) + return WPA_CIPHER_WEP40; + 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_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", + 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/src/common/wpa_common.h b/src/common/wpa_common.h index 20c79d8099b3..091e317fdd68 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -1,6 +1,6 @@ /* * WPA definitions shared between hostapd and wpa_supplicant - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -20,6 +20,14 @@ #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_WEP104 | \ +WPA_CIPHER_WEP40 | 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 @@ -54,7 +62,12 @@ #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_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -64,11 +77,14 @@ #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) #define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) -#ifdef CONFIG_IEEE80211W #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) -#endif /* CONFIG_IEEE80211W */ #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. @@ -92,6 +108,9 @@ #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)) @@ -109,6 +128,7 @@ #ifdef CONFIG_IEEE80211W #define WPA_IGTK_LEN 16 +#define WPA_IGTK_MAX_LEN 32 #endif /* CONFIG_IEEE80211W */ @@ -137,6 +157,7 @@ /* 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 @@ -170,22 +191,38 @@ struct wpa_eapol_key { /* 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[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ - u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ - u8 tk1[16]; /* Temporal Key 1 (TK1) */ - union { - u8 tk2[16]; /* Temporal Key 2 (TK2) */ - struct { - u8 tx_mic_key[8]; - u8 rx_mic_key[8]; - } auth; - } u; -} STRUCT_PACKED; + 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 @@ -263,10 +300,11 @@ struct rsn_error_kde { } 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_LEN]; + u8 igtk[WPA_IGTK_MAX_LEN]; } STRUCT_PACKED; #endif /* CONFIG_IEEE80211W */ @@ -305,16 +343,17 @@ struct rsn_rdie { #endif /* _MSC_VER */ -int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, - u8 *mic); -void 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, - u8 *ptk, size_t ptk_len, int use_sha256); +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, const u8 *sta_addr, const u8 *ap_addr, - u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, +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); @@ -327,10 +366,10 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, 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); -void 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, - u8 *ptk, size_t ptk_len, u8 *ptk_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 { @@ -352,9 +391,30 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, 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); @@ -387,8 +447,15 @@ 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_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/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 58cbe6a0d1c9..ccaaf1b056b5 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -25,6 +25,10 @@ #include "private/android_filesystem_config.h" #endif /* ANDROID */ +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 +#include +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + #include "wpa_ctrl.h" #include "common.h" @@ -46,8 +50,13 @@ struct wpa_ctrl { #ifdef CONFIG_CTRL_IFACE_UDP int s; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 local; + struct sockaddr_in6 dest; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in local; struct sockaddr_in dest; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ char *cookie; char *remote_ifname; char *remote_ip; @@ -82,10 +91,12 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) int tries = 0; int flags; - ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl_path == NULL) + return NULL; + + ctrl = os_zalloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; - os_memset(ctrl, 0, sizeof(*ctrl)); ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); if (ctrl->s < 0) { @@ -100,7 +111,7 @@ try_again: CONFIG_CTRL_IFACE_CLIENT_DIR "/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", (int) getpid(), counter); - if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { + if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { close(ctrl->s); os_free(ctrl); return NULL; @@ -126,13 +137,27 @@ try_again: #ifdef ANDROID chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + + if (os_strncmp(ctrl_path, "@android:", 9) == 0) { + if (socket_local_client_connect( + ctrl->s, ctrl_path + 9, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } + /* * If the ctrl_path isn't an absolute pathname, assume that * it's the name of a socket in the Android reserved namespace. * Otherwise, it's a normal UNIX domain socket appearing in the * filesystem. */ - if (ctrl_path != NULL && *ctrl_path != '/') { + if (*ctrl_path != '/') { char buf[21]; os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); if (socket_local_client_connect( @@ -149,12 +174,18 @@ try_again: #endif /* ANDROID */ ctrl->dest.sun_family = AF_UNIX; - res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, - sizeof(ctrl->dest.sun_path)); - if (res >= sizeof(ctrl->dest.sun_path)) { - close(ctrl->s); - os_free(ctrl); - return NULL; + if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) { + ctrl->dest.sun_path[0] = '\0'; + os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, + sizeof(ctrl->dest.sun_path) - 1); + } else { + res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, + sizeof(ctrl->dest.sun_path)); + if (res >= sizeof(ctrl->dest.sun_path)) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } } if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { @@ -206,7 +237,6 @@ void wpa_ctrl_cleanup(void) struct dirent entry; struct dirent *result; size_t dirnamelen; - int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX); size_t maxcopy; char pathname[PATH_MAX]; char *namep; @@ -223,11 +253,8 @@ void wpa_ctrl_cleanup(void) namep = pathname + dirnamelen; maxcopy = PATH_MAX - dirnamelen; while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { - if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX, - prefixlen) == 0) { - if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) - unlink(pathname); - } + if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) + unlink(pathname); } closedir(dir); } @@ -255,24 +282,37 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) struct hostent *h; #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ - ctrl = os_malloc(sizeof(*ctrl)); + ctrl = os_zalloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; - os_memset(ctrl, 0, sizeof(*ctrl)); +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (ctrl->s < 0) { perror("socket"); os_free(ctrl); return NULL; } +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->local.sin6_family = AF_INET6; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin6_addr = in6addr_any; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ctrl->local.sin_family = AF_INET; #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE ctrl->local.sin_addr.s_addr = INADDR_ANY; #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { close(ctrl->s); @@ -280,14 +320,24 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) return NULL; } +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->dest.sin6_family = AF_INET6; + inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr); + ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ctrl->dest.sin_family = AF_INET; ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE if (ctrl_path) { char *port, *name; int port_id; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char *scope; + int scope_id = 0; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ name = os_strdup(ctrl_path); if (name == NULL) { @@ -295,7 +345,11 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) os_free(ctrl); return NULL; } +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + port = os_strchr(name, ','); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ port = os_strchr(name, ':'); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (port) { port_id = atoi(&port[1]); @@ -303,7 +357,16 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) } else port_id = WPA_CTRL_IFACE_PORT; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + scope = os_strchr(name, '%'); + if (scope) { + scope_id = if_nametoindex(&scope[1]); + scope[0] = '\0'; + } + h = gethostbyname2(name, AF_INET6); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ h = gethostbyname(name); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ctrl->remote_ip = os_strdup(name); os_free(name); if (h == NULL) { @@ -313,16 +376,33 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) os_free(ctrl); return NULL; } +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->dest.sin6_scope_id = scope_id; + ctrl->dest.sin6_port = htons(port_id); + os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ ctrl->dest.sin_port = htons(port_id); - os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr, - h->h_length); + os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ } else ctrl->remote_ip = os_strdup("localhost"); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { - perror("connect"); +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char addr[INET6_ADDRSTRLEN]; + wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s", + inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr, + sizeof(ctrl->dest)), + ntohs(ctrl->dest.sin6_port), + strerror(errno)); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s", + inet_ntoa(ctrl->dest.sin_addr), + ntohs(ctrl->dest.sin_port), + strerror(errno)); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ close(ctrl->s); os_free(ctrl->remote_ip); os_free(ctrl); @@ -372,7 +452,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; - struct os_time started_at; + struct os_reltime started_at; int res; fd_set rfds; const char *_cmd; @@ -411,12 +491,12 @@ retry_send: * longer before giving up. */ if (started_at.sec == 0) - os_get_time(&started_at); + os_get_reltime(&started_at); else { - struct os_time n; - os_get_time(&n); + struct os_reltime n; + os_get_reltime(&n); /* Try for a few seconds. */ - if (n.sec > started_at.sec + 5) + if (os_reltime_expired(&n, &started_at, 5)) goto send_err; } os_sleep(1, 0); @@ -561,7 +641,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", ctrl_path); #endif /* UNICODE */ - if (ret < 0 || ret >= 256) { + if (os_snprintf_error(256, ret)) { os_free(ctrl); return NULL; } diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index bb9b5587f26b..1d19fc550df6 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -42,8 +42,12 @@ extern "C" { #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) */ @@ -52,15 +56,34 @@ extern "C" { #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 " +/** 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 */ @@ -82,6 +105,10 @@ extern "C" { #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 " @@ -95,6 +122,20 @@ extern "C" { #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 " @@ -126,14 +167,55 @@ extern "C" { #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " /* parameters: */ #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: */ +#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: */ +#define CRED_ADDED "CRED-ADDED " +/* Credential block modified; parameters: */ +#define CRED_MODIFIED "CRED-MODIFIED " +/* Credential block removed; parameters: */ +#define CRED_REMOVED "CRED-REMOVED " #define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " +/* parameters: */ +#define GAS_QUERY_START "GAS-QUERY-START " +/* parameters: */ +#define GAS_QUERY_DONE "GAS-QUERY-DONE " + +/* parameters: */ +#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 " @@ -146,10 +228,33 @@ extern "C" { #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +#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 0xFFFFFFFF +#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) @@ -167,6 +272,30 @@ extern "C" { #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) + + +/* 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 */ @@ -256,9 +385,10 @@ int wpa_ctrl_detach(struct wpa_ctrl *ctrl); * @reply_len: Length of the reply buffer * Returns: 0 on success, -1 on failure * - * This function will receive a pending control interface message. This - * function will block if no messages are available. The received response will - * be written to reply and reply_len is set to the actual length of the reply. + * 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. */ @@ -293,8 +423,6 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); -char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); - #ifdef ANDROID /** * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that @@ -312,6 +440,8 @@ void wpa_ctrl_cleanup(void); #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 */ diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c new file mode 100644 index 000000000000..28913b9139aa --- /dev/null +++ b/src/common/wpa_helpers.c @@ -0,0 +1,292 @@ +/* + * wpa_supplicant ctrl_iface helpers + * Copyright (c) 2010-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. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "wpa_ctrl.h" +#include "wpa_helpers.h" + + +char *wpas_ctrl_path = "/var/run/wpa_supplicant/"; +static int default_timeout = 60; + + +static struct wpa_ctrl * wpa_open_ctrl(const char *ifname) +{ + char buf[128]; + struct wpa_ctrl *ctrl; + + os_snprintf(buf, sizeof(buf), "%s%s", wpas_ctrl_path, ifname); + ctrl = wpa_ctrl_open(buf); + if (ctrl == NULL) + printf("wpa_command: wpa_ctrl_open(%s) failed\n", buf); + return ctrl; +} + + +int wpa_command(const char *ifname, const char *cmd) +{ + struct wpa_ctrl *ctrl; + char buf[128]; + size_t len; + + printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd); + ctrl = wpa_open_ctrl(ifname); + if (ctrl == NULL) + return -1; + len = sizeof(buf); + if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL) < 0) { + printf("wpa_command: wpa_ctrl_request failed\n"); + wpa_ctrl_close(ctrl); + return -1; + } + wpa_ctrl_close(ctrl); + buf[len] = '\0'; + if (strncmp(buf, "FAIL", 4) == 0) { + printf("wpa_command: Command failed (FAIL received)\n"); + return -1; + } + return 0; +} + + +int wpa_command_resp(const char *ifname, const char *cmd, + char *resp, size_t resp_size) +{ + struct wpa_ctrl *ctrl; + size_t len; + + printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd); + ctrl = wpa_open_ctrl(ifname); + if (ctrl == NULL) + return -1; + len = resp_size; + if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), resp, &len, NULL) < 0) { + printf("wpa_command: wpa_ctrl_request failed\n"); + wpa_ctrl_close(ctrl); + return -1; + } + wpa_ctrl_close(ctrl); + resp[len] = '\0'; + return 0; +} + + +struct wpa_ctrl * open_wpa_mon(const char *ifname) +{ + struct wpa_ctrl *ctrl; + + ctrl = wpa_open_ctrl(ifname); + if (ctrl == NULL) + return NULL; + if (wpa_ctrl_attach(ctrl) < 0) { + wpa_ctrl_close(ctrl); + return NULL; + } + + return ctrl; +} + + +int get_wpa_cli_event2(struct wpa_ctrl *mon, + const char *event, const char *event2, + char *buf, size_t buf_size) +{ + int fd, ret; + fd_set rfd; + char *pos; + struct timeval tv; + time_t start, now; + + printf("Waiting for wpa_cli event %s\n", event); + fd = wpa_ctrl_get_fd(mon); + if (fd < 0) + return -1; + + time(&start); + while (1) { + size_t len; + + FD_ZERO(&rfd); + FD_SET(fd, &rfd); + tv.tv_sec = default_timeout; + tv.tv_usec = 0; + ret = select(fd + 1, &rfd, NULL, NULL, &tv); + if (ret == 0) { + printf("Timeout on waiting for event %s\n", event); + return -1; + } + if (ret < 0) { + printf("select: %s\n", strerror(errno)); + return -1; + } + len = buf_size; + if (wpa_ctrl_recv(mon, buf, &len) < 0) { + printf("Failure while waiting for event %s\n", event); + return -1; + } + if (len == buf_size) + len--; + buf[len] = '\0'; + + pos = strchr(buf, '>'); + if (pos && + (strncmp(pos + 1, event, strlen(event)) == 0 || + (event2 && + strncmp(pos + 1, event2, strlen(event2)) == 0))) + return 0; /* Event found */ + + time(&now); + if ((int) (now - start) > default_timeout) { + printf("Timeout on waiting for event %s\n", event); + return -1; + } + } +} + + +int get_wpa_cli_event(struct wpa_ctrl *mon, + const char *event, char *buf, size_t buf_size) +{ + return get_wpa_cli_event2(mon, event, NULL, buf, buf_size); +} + + +int get_wpa_status(const char *ifname, const char *field, char *obuf, + size_t obuf_size) +{ + struct wpa_ctrl *ctrl; + char buf[4096]; + char *pos, *end; + size_t len, flen; + + ctrl = wpa_open_ctrl(ifname); + if (ctrl == NULL) + return -1; + len = sizeof(buf); + if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) { + wpa_ctrl_close(ctrl); + return -1; + } + wpa_ctrl_close(ctrl); + buf[len] = '\0'; + + flen = strlen(field); + pos = buf; + while (pos + flen < buf + len) { + if (pos > buf) { + if (*pos != '\n') { + pos++; + continue; + } + pos++; + } + if (strncmp(pos, field, flen) != 0 || pos[flen] != '=') { + pos++; + continue; + } + pos += flen + 1; + end = strchr(pos, '\n'); + if (end == NULL) + return -1; + *end++ = '\0'; + if (end - pos > (int) obuf_size) + return -1; + memcpy(obuf, pos, end - pos); + return 0; + } + + return -1; +} + + +int wait_ip_addr(const char *ifname, int timeout) +{ + char ip[30]; + int count = timeout; + struct wpa_ctrl *ctrl; + + while (count > 0) { + printf("%s: ifname='%s' - %d seconds remaining\n", + __func__, ifname, count); + count--; + if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0 + && strlen(ip) > 0) { + printf("IP address found: '%s'\n", ip); + return 0; + } + ctrl = wpa_open_ctrl(ifname); + if (ctrl == NULL) + return -1; + wpa_ctrl_close(ctrl); + sleep(1); + } + printf("%s: Could not get IP address for ifname='%s'", __func__, + ifname); + return -1; +} + + +int add_network(const char *ifname) +{ + char res[30]; + + if (wpa_command_resp(ifname, "ADD_NETWORK", res, sizeof(res)) < 0) + return -1; + return atoi(res); +} + + +int set_network(const char *ifname, int id, const char *field, + const char *value) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "SET_NETWORK %d %s %s", id, field, value); + return wpa_command(ifname, buf); +} + + +int set_network_quoted(const char *ifname, int id, const char *field, + const char *value) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "SET_NETWORK %d %s \"%s\"", + id, field, value); + return wpa_command(ifname, buf); +} + + +int add_cred(const char *ifname) +{ + char res[30]; + + if (wpa_command_resp(ifname, "ADD_CRED", res, sizeof(res)) < 0) + return -1; + return atoi(res); +} + + +int set_cred(const char *ifname, int id, const char *field, const char *value) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "SET_CRED %d %s %s", id, field, value); + return wpa_command(ifname, buf); +} + + +int set_cred_quoted(const char *ifname, int id, const char *field, + const char *value) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "SET_CRED %d %s \"%s\"", + id, field, value); + return wpa_command(ifname, buf); +} diff --git a/src/common/wpa_helpers.h b/src/common/wpa_helpers.h new file mode 100644 index 000000000000..54c2872fbc5e --- /dev/null +++ b/src/common/wpa_helpers.h @@ -0,0 +1,37 @@ +/* + * wpa_supplicant ctrl_iface helpers + * Copyright (c) 2010-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 WPA_HELPERS_H +#define WPA_HELPERS_H + +int wpa_command(const char *ifname, const char *cmd); +int wpa_command_resp(const char *ifname, const char *cmd, + char *resp, size_t resp_size); +int get_wpa_status(const char *ifname, const char *field, char *obuf, + size_t obuf_size); + +struct wpa_ctrl * open_wpa_mon(const char *ifname); +int wait_ip_addr(const char *ifname, int timeout); +int get_wpa_cli_event(struct wpa_ctrl *mon, + const char *event, char *buf, size_t buf_size); +int get_wpa_cli_event2(struct wpa_ctrl *mon, + const char *event, const char *event2, + char *buf, size_t buf_size); + +int add_network(const char *ifname); +int set_network(const char *ifname, int id, const char *field, + const char *value); +int set_network_quoted(const char *ifname, int id, const char *field, + const char *value); +int add_cred(const char *ifname); +int set_cred(const char *ifname, int id, const char *field, const char *value); +int set_cred_quoted(const char *ifname, int id, const char *field, + const char *value); + +#endif /* WPA_HELPERS_H */ diff --git a/src/crypto/Makefile b/src/crypto/Makefile index a605a65cd821..3e90350c103e 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -1,7 +1,7 @@ all: libcrypto.a clean: - rm -f *~ *.o *.d libcrypto.a + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a install: @echo Nothing to be made. @@ -9,6 +9,7 @@ install: include ../lib.rules +CFLAGS += -DCONFIG_CRYPTO_INTERNAL CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER #CFLAGS += -DALL_DH_GROUPS @@ -25,6 +26,7 @@ LIB_OBJS= \ aes-internal-dec.o \ aes-internal-enc.o \ aes-omac1.o \ + aes-siv.o \ aes-unwrap.o \ aes-wrap.o \ des-internal.o \ diff --git a/src/crypto/aes-ccm.c b/src/crypto/aes-ccm.c index d14670db8d65..cf227785c3ca 100644 --- a/src/crypto/aes-ccm.c +++ b/src/crypto/aes-ccm.c @@ -203,7 +203,7 @@ int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, aes_encrypt_deinit(aes); - if (os_memcmp(x, t, M) != 0) { + if (os_memcmp_const(x, t, M) != 0) { wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch"); return -1; } diff --git a/src/crypto/aes-eax.c b/src/crypto/aes-eax.c index 21941c66de5d..15a09f8b461a 100644 --- a/src/crypto/aes-eax.c +++ b/src/crypto/aes-eax.c @@ -71,7 +71,7 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, ret = 0; fail: - os_free(buf); + bin_clear_free(buf, buf_len); return ret; } diff --git a/src/crypto/aes-gcm.c b/src/crypto/aes-gcm.c index 3d91c71de2dd..84294d2d104c 100644 --- a/src/crypto/aes-gcm.c +++ b/src/crypto/aes-gcm.c @@ -310,7 +310,7 @@ int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, aes_encrypt_deinit(aes); - if (os_memcmp(tag, T, 16) != 0) { + if (os_memcmp_const(tag, T, 16) != 0) { wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch"); return -1; } diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c index 27895eb007ef..375db5735be3 100644 --- a/src/crypto/aes-omac1.c +++ b/src/crypto/aes-omac1.c @@ -1,5 +1,5 @@ /* - * One-key CBC MAC (OMAC1) hash with AES-128 + * One-key CBC MAC (OMAC1) hash with AES * * Copyright (c) 2003-2007, Jouni Malinen * @@ -27,8 +27,9 @@ static void gf_mulx(u8 *pad) /** - * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128 - * @key: 128-bit key for the hash operation + * 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 @@ -39,15 +40,15 @@ static void gf_mulx(u8 *pad) * 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) +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, 16); + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; os_memset(cbc, 0, AES_BLOCK_SIZE); @@ -65,6 +66,13 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem, 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]; @@ -83,6 +91,12 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem, 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]; @@ -100,6 +114,26 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem, } +/** + * 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 @@ -116,3 +150,21 @@ 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/src/crypto/aes-siv.c b/src/crypto/aes-siv.c new file mode 100644 index 000000000000..5ac82c2e4b5c --- /dev/null +++ b/src/crypto/aes-siv.c @@ -0,0 +1,188 @@ +/* + * AES SIV (RFC 5297) + * Copyright (c) 2013 Cozybit, 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 "aes.h" +#include "aes_wrap.h" +#include "aes_siv.h" + + +static const u8 zero[AES_BLOCK_SIZE]; + + +static void dbl(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; +} + + +static void xor(u8 *a, const u8 *b) +{ + int i; + + for (i = 0; i < AES_BLOCK_SIZE; i++) + *a++ ^= *b++; +} + + +static void xorend(u8 *a, int alen, const u8 *b, int blen) +{ + int i; + + if (alen < blen) + return; + + for (i = 0; i < blen; i++) + a[alen - blen + i] ^= b[i]; +} + + +static void pad_block(u8 *pad, const u8 *addr, size_t len) +{ + os_memset(pad, 0, AES_BLOCK_SIZE); + os_memcpy(pad, addr, len); + + if (len < AES_BLOCK_SIZE) + pad[len] = 0x80; +} + + +static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], + size_t *len, u8 *mac) +{ + u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; + u8 *buf = NULL; + int ret; + size_t i; + + if (!num_elem) { + os_memcpy(tmp, zero, sizeof(zero)); + tmp[AES_BLOCK_SIZE - 1] = 1; + return omac1_aes_128(key, tmp, sizeof(tmp), mac); + } + + ret = omac1_aes_128(key, zero, sizeof(zero), tmp); + if (ret) + return ret; + + for (i = 0; i < num_elem - 1; i++) { + ret = omac1_aes_128(key, addr[i], len[i], tmp2); + if (ret) + return ret; + + dbl(tmp); + xor(tmp, tmp2); + } + if (len[i] >= AES_BLOCK_SIZE) { + buf = os_malloc(len[i]); + if (!buf) + return -ENOMEM; + + os_memcpy(buf, addr[i], len[i]); + xorend(buf, len[i], tmp, AES_BLOCK_SIZE); + ret = omac1_aes_128(key, buf, len[i], mac); + bin_clear_free(buf, len[i]); + return ret; + } + + dbl(tmp); + pad_block(tmp2, addr[i], len[i]); + xor(tmp, tmp2); + + return omac1_aes_128(key, tmp, sizeof(tmp), mac); +} + + +int aes_siv_encrypt(const u8 *key, const u8 *pw, + size_t pwlen, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *out) +{ + const u8 *_addr[6]; + size_t _len[6]; + const u8 *k1 = key, *k2 = key + 16; + u8 v[AES_BLOCK_SIZE]; + size_t i; + u8 *iv, *crypt_pw; + + if (num_elem > ARRAY_SIZE(_addr) - 1) + return -1; + + for (i = 0; i < num_elem; i++) { + _addr[i] = addr[i]; + _len[i] = len[i]; + } + _addr[num_elem] = pw; + _len[num_elem] = pwlen; + + if (aes_s2v(k1, num_elem + 1, _addr, _len, v)) + return -1; + + iv = out; + crypt_pw = out + AES_BLOCK_SIZE; + + os_memcpy(iv, v, AES_BLOCK_SIZE); + os_memcpy(crypt_pw, pw, pwlen); + + /* zero out 63rd and 31st bits of ctr (from right) */ + v[8] &= 0x7f; + v[12] &= 0x7f; + return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen); +} + + +int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out) +{ + const u8 *_addr[6]; + size_t _len[6]; + const u8 *k1 = key, *k2 = key + 16; + size_t crypt_len; + size_t i; + int ret; + u8 iv[AES_BLOCK_SIZE]; + u8 check[AES_BLOCK_SIZE]; + + if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1) + return -1; + crypt_len = iv_c_len - AES_BLOCK_SIZE; + + for (i = 0; i < num_elem; i++) { + _addr[i] = addr[i]; + _len[i] = len[i]; + } + _addr[num_elem] = out; + _len[num_elem] = crypt_len; + + os_memcpy(iv, iv_crypt, AES_BLOCK_SIZE); + os_memcpy(out, iv_crypt + AES_BLOCK_SIZE, crypt_len); + + iv[8] &= 0x7f; + iv[12] &= 0x7f; + + ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len); + if (ret) + return ret; + + ret = aes_s2v(k1, num_elem + 1, _addr, _len, check); + if (ret) + return ret; + if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0) + return 0; + + return -1; +} diff --git a/src/crypto/aes-unwrap.c b/src/crypto/aes-unwrap.c index 9dd51602f358..ec793d9dbf78 100644 --- a/src/crypto/aes-unwrap.c +++ b/src/crypto/aes-unwrap.c @@ -1,5 +1,5 @@ /* - * AES key unwrap (128-bit KEK, RFC3394) + * AES key unwrap (RFC3394) * * Copyright (c) 2003-2007, Jouni Malinen * @@ -14,26 +14,29 @@ #include "aes_wrap.h" /** - * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * 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, int n, const u8 *cipher, u8 *plain) +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) { - u8 a[8], *r, b[16]; + 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, 16); + ctx = aes_decrypt_init(kek, kek_len); if (ctx == NULL) return -1; @@ -48,7 +51,11 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) r = plain + (n - 1) * 8; for (i = n; i >= 1; i--) { os_memcpy(b, a, 8); - b[7] ^= n * j + i; + 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); diff --git a/src/crypto/aes-wrap.c b/src/crypto/aes-wrap.c index 89d6f94bf761..7ed34e803e4e 100644 --- a/src/crypto/aes-wrap.c +++ b/src/crypto/aes-wrap.c @@ -1,5 +1,5 @@ /* - * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * AES Key Wrap Algorithm (RFC3394) * * Copyright (c) 2003-2007, Jouni Malinen * @@ -14,19 +14,21 @@ #include "aes_wrap.h" /** - * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * @kek: 16-octet Key encryption key (KEK) + * aes_wrap - Wrap keys 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 * @plain: Plaintext key to be wrapped, n * 64 bits * @cipher: Wrapped key, (n + 1) * 64 bits * Returns: 0 on success, -1 on failure */ -int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) { - u8 *a, *r, b[16]; + u8 *a, *r, b[AES_BLOCK_SIZE]; int i, j; void *ctx; + unsigned int t; a = cipher; r = cipher + 8; @@ -35,7 +37,7 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) os_memset(a, 0xa6, 8); os_memcpy(r, plain, 8 * n); - ctx = aes_encrypt_init(kek, 16); + ctx = aes_encrypt_init(kek, kek_len); if (ctx == NULL) return -1; @@ -53,7 +55,11 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) os_memcpy(b + 8, r, 8); aes_encrypt(ctx, b, b); os_memcpy(a, b, 8); - a[7] ^= n * j + i; + t = n * j + i; + a[7] ^= t; + a[6] ^= t >> 8; + a[5] ^= t >> 16; + a[4] ^= t >> 24; os_memcpy(r, b + 8, 8); r += 8; } diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h new file mode 100644 index 000000000000..463cf6536107 --- /dev/null +++ b/src/crypto/aes_siv.h @@ -0,0 +1,19 @@ +/* + * AES SIV (RFC 5297) + * Copyright (c) 2013 Cozybit, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AES_SIV_H +#define AES_SIV_H + +int aes_siv_encrypt(const u8 *key, const u8 *pw, + size_t pwlen, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *out); +int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out); + +#endif /* AES_SIV_H */ diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h index 0433c0434eed..4a142093b0d6 100644 --- a/src/crypto/aes_wrap.h +++ b/src/crypto/aes_wrap.h @@ -1,8 +1,8 @@ /* * AES-based functions * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - 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 @@ -18,13 +18,20 @@ #ifndef AES_WRAP_H #define AES_WRAP_H -int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); -int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +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); diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 26b9acf2f424..f2d5662ff01e 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2009, Jouni Malinen + * Wrapper functions for crypto libraries + * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -271,6 +271,10 @@ struct crypto_private_key; */ 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) @@ -457,4 +461,319 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, */ 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); + +/** + * 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_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); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c index 54209fad3c01..dc7f350af057 100644 --- a/src/crypto/crypto_internal-rsa.c +++ b/src/crypto/crypto_internal-rsa.c @@ -26,6 +26,15 @@ 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) +{ + return (struct crypto_public_key *) + crypto_rsa_import_public_key_parts(n, n_len, e, e_len); +} + + struct crypto_private_key * crypto_private_key_import(const u8 *key, size_t len, const char *passwd) diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c new file mode 100644 index 000000000000..7137c27d0e8c --- /dev/null +++ b/src/crypto/crypto_module_tests.c @@ -0,0 +1,1679 @@ +/* + * crypto module tests + * Copyright (c) 2014-2015, Jouni Malinen + * + * 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 "crypto/aes_siv.h" +#include "crypto/aes_wrap.h" +#include "crypto/aes.h" +#include "crypto/ms_funcs.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" + + +static int test_siv(void) +{ +#ifdef CONFIG_MESH + /* RFC 5297, A.1. Deterministic Authenticated Encryption Example */ + u8 key[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + u8 ad[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + u8 plaintext[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + }; + u8 iv_c[] = { + 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, + 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, + 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, + 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c + }; + /* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */ + u8 key_2[] = { + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; + u8 ad1_2[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 + }; + u8 ad2_2[] = { + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, + 0x90, 0xa0 + }; + u8 nonce_2[] = { + 0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b, + 0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0 + }; + u8 plaintext_2[] = { + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61, + 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, + 0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53 + }; + u8 iv_c_2[] = { + 0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb, + 0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f, + 0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43, + 0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17, + 0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63, + 0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29, + 0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c, + 0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d + }; + u8 out[2 * AES_BLOCK_SIZE + sizeof(plaintext_2)]; + const u8 *addr[3]; + size_t len[3]; + + /* RFC 5297, A.1. Deterministic Authenticated Encryption Example */ + addr[0] = ad; + len[0] = sizeof(ad); + + if (aes_siv_encrypt(key, plaintext, sizeof(plaintext), + 1, addr, len, out)) { + wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); + return 1; + } + if (os_memcmp(out, iv_c, sizeof(iv_c)) != 0) { + wpa_printf(MSG_ERROR, + "AES-SIV mode encryption returned invalid cipher text"); + return 1; + } + + if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) { + wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); + return 1; + } + if (os_memcmp(out, plaintext, sizeof(plaintext)) != 0) { + wpa_printf(MSG_ERROR, + "AES-SIV mode decryption returned invalid plain text"); + return 1; + } + + /* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */ + addr[0] = ad1_2; + len[0] = sizeof(ad1_2); + addr[1] = ad2_2; + len[1] = sizeof(ad2_2); + addr[2] = nonce_2; + len[2] = sizeof(nonce_2); + + if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2), + 3, addr, len, out)) { + wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); + return 1; + } + if (os_memcmp(out, iv_c_2, sizeof(iv_c_2)) != 0) { + wpa_printf(MSG_ERROR, + "AES-SIV mode encryption returned invalid cipher text"); + return 1; + } + + if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) { + wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); + return 1; + } + if (os_memcmp(out, plaintext_2, sizeof(plaintext_2)) != 0) { + wpa_printf(MSG_ERROR, + "AES-SIV mode decryption returned invalid plain text"); + return 1; + } + + wpa_printf(MSG_INFO, "AES-SIV test cases passed"); +#endif /* CONFIG_MESH */ + + return 0; +} + + +/* OMAC1 AES-128 test vectors from + * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf + * which are same as the examples from NIST SP800-38B + * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf + */ + +struct omac1_test_vector { + u8 k[16]; + u8 msg[64]; + int msg_len; + u8 tag[16]; +}; + +static struct omac1_test_vector omac1_test_vectors[] = +{ + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { }, + 0, + { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, + 16, + { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 }, + 40, + { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, + 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 } + }, + { + { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, + { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }, + 64, + { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe } + }, +}; + + +static int test_omac1_vector(struct omac1_test_vector *tv, unsigned int i) +{ + u8 key[] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + u8 msg[] = { 0x12, 0x34, 0x56 }; + u8 result[24], result2[24]; + const u8 *addr[3]; + size_t len[3]; + + if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) || + os_memcmp(result, tv->tag, 16) != 0) { + wpa_printf(MSG_ERROR, "OMAC1-AES-128 test vector %u failed", i); + return 1; + } + + if (tv->msg_len > 1) { + + addr[0] = tv->msg; + len[0] = 1; + addr[1] = tv->msg + 1; + len[1] = tv->msg_len - 1; + + if (omac1_aes_128_vector(tv->k, 2, addr, len, result) || + os_memcmp(result, tv->tag, 16) != 0) { + wpa_printf(MSG_ERROR, + "OMAC1-AES-128(vector) test vector %u failed", + i); + return 1; + } + + addr[0] = tv->msg; + len[0] = tv->msg_len - 2; + addr[1] = tv->msg + tv->msg_len - 2; + len[1] = 1; + addr[2] = tv->msg + tv->msg_len - 1; + len[2] = 1; + + if (omac1_aes_128_vector(tv->k, 3, addr, len, result) || + os_memcmp(result, tv->tag, 16) != 0) { + wpa_printf(MSG_ERROR, + "OMAC1-AES-128(vector2) test vector %u failed", + i); + return 1; + } + } + + addr[0] = &msg[0]; + len[0] = 1; + addr[1] = &msg[1]; + len[1] = 1; + addr[2] = &msg[2]; + len[2] = 1; + if (omac1_aes_128(key, msg, sizeof(msg), result) || + omac1_aes_128_vector(key, 3, addr, len, result2) || + os_memcmp(result, result2, 16) != 0) { + wpa_printf(MSG_ERROR, "OMAC1-AES-128 short test mismatch"); + return 1; + } + + return 0; +} + + +static int test_omac1(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(omac1_test_vectors); i++) { + if (test_omac1_vector(&omac1_test_vectors[i], i)) + return 1; + } + + wpa_printf(MSG_INFO, "OMAC1-AES-128 test cases passed"); + + return 0; +} + + +static int test_eax(void) +{ +#ifdef EAP_PSK + u8 msg[] = { 0xF7, 0xFB }; + u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, + 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 }; + u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, + 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD }; + u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA }; + u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, + 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, + 0x67, 0xE5 }; + u8 data[sizeof(msg)], tag[AES_BLOCK_SIZE]; + + os_memcpy(data, msg, sizeof(msg)); + if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + wpa_printf(MSG_ERROR, "AES-128 EAX mode encryption failed"); + return 1; + } + if (os_memcmp(data, cipher, sizeof(data)) != 0) { + wpa_printf(MSG_ERROR, + "AES-128 EAX mode encryption returned invalid cipher text"); + return 1; + } + if (os_memcmp(tag, cipher + sizeof(data), AES_BLOCK_SIZE) != 0) { + wpa_printf(MSG_ERROR, + "AES-128 EAX mode encryption returned invalid tag"); + return 1; + } + + if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), + data, sizeof(data), tag)) { + wpa_printf(MSG_ERROR, "AES-128 EAX mode decryption failed"); + return 1; + } + if (os_memcmp(data, msg, sizeof(data)) != 0) { + wpa_printf(MSG_ERROR, + "AES-128 EAX mode decryption returned invalid plain text"); + return 1; + } + + wpa_printf(MSG_INFO, "AES-128 EAX mode test cases passed"); +#endif /* EAP_PSK */ + + return 0; +} + + +static int test_cbc(void) +{ + struct cbc_test_vector { + u8 key[16]; + u8 iv[16]; + u8 plain[32]; + u8 cipher[32]; + size_t len; + } vectors[] = { + { + { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }, + { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }, + "Single block msg", + { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }, + 16 + }, + { + { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, + 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a }, + { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, + 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, + { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, + 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, + 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, + 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }, + 32 + } + }; + int ret = 0; + u8 *buf; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vectors); i++) { + struct cbc_test_vector *tv = &vectors[i]; + + buf = os_malloc(tv->len); + if (buf == NULL) { + ret++; + break; + } + + os_memcpy(buf, tv->plain, tv->len); + if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) || + os_memcmp(buf, tv->cipher, tv->len) != 0) { + wpa_printf(MSG_ERROR, "AES-CBC encrypt %d failed", i); + ret++; + } + + os_memcpy(buf, tv->cipher, tv->len); + if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) || + os_memcmp(buf, tv->plain, tv->len) != 0) { + wpa_printf(MSG_ERROR, "AES-CBC decrypt %d failed", i); + ret++; + } + + os_free(buf); + } + + return ret; +} + + +static int test_ecb(void) +{ +#ifdef EAP_PSK + struct ecb_test_vector { + char *key; + char *plaintext; + char *ciphertext; + } vectors[] = { + /* CAVS 11.1 - ECBGFSbox128.rsp */ + { + "00000000000000000000000000000000", + "f34481ec3cc627bacd5dc3fb08f273e6", + "0336763e966d92595a567cc9ce537f5e" + }, + { + "00000000000000000000000000000000", + "9798c4640bad75c7c3227db910174e72", + "a9a1631bf4996954ebc093957b234589" + }, + { + "00000000000000000000000000000000", + "96ab5c2ff612d9dfaae8c31f30c42168", + "ff4f8391a6a40ca5b25d23bedd44a597" + }, + { + "00000000000000000000000000000000", + "6a118a874519e64e9963798a503f1d35", + "dc43be40be0e53712f7e2bf5ca707209" + }, + { + "00000000000000000000000000000000", + "cb9fceec81286ca3e989bd979b0cb284", + "92beedab1895a94faa69b632e5cc47ce" + }, + { + "00000000000000000000000000000000", + "b26aeb1874e47ca8358ff22378f09144", + "459264f4798f6a78bacb89c15ed3d601" + }, + { + "00000000000000000000000000000000", + "58c8e00b2631686d54eab84b91f0aca1", + "08a4e2efec8a8e3312ca7460b9040bbf" + }, + /* CAVS 11.1 - ECBKeySbox128.rsp */ + { + "10a58869d74be5a374cf867cfb473859", + "00000000000000000000000000000000", + "6d251e6944b051e04eaa6fb4dbf78465" + }, + { + "caea65cdbb75e9169ecd22ebe6e54675", + "00000000000000000000000000000000", + "6e29201190152df4ee058139def610bb", + } + }; + int ret = 0; + unsigned int i; + u8 key[16], plain[16], cipher[16], out[16]; + + for (i = 0; i < ARRAY_SIZE(vectors); i++) { + struct ecb_test_vector *tv = &vectors[i]; + + if (hexstr2bin(tv->key, key, sizeof(key)) || + hexstr2bin(tv->plaintext, plain, sizeof(plain)) || + hexstr2bin(tv->ciphertext, cipher, sizeof(cipher))) { + wpa_printf(MSG_ERROR, "Invalid AES-ECB test vector %u", + i); + ret++; + continue; + } + + if (aes_128_encrypt_block(key, plain, out) < 0 || + os_memcmp(out, cipher, 16) != 0) { + wpa_printf(MSG_ERROR, "AES-ECB encrypt %u failed", i); + ret++; + } + } + + if (!ret) + wpa_printf(MSG_INFO, "AES ECB mode test cases passed"); + + return ret; +#endif /* EAP_PSK */ + + return 0; +} + + +static int test_key_wrap(void) +{ + int ret = 0; + + /* RFC 3394 - Test vector 4.1 */ + u8 kek41[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + u8 plain41[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + u8 crypt41[] = { + 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, + 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, + 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 + }; + /* RFC 3394 - Test vector 4.2 */ + u8 kek42[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 + }; + u8 plain42[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + u8 crypt42[] = { + 0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35, + 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, + 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D + }; + /* RFC 3394 - Test vector 4.3 */ + u8 kek43[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + u8 plain43[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + u8 crypt43[] = { + 0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, + 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, + 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7, + }; + /* RFC 3394 - Test vector 4.4 */ + u8 kek44[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 + }; + u8 plain44[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + u8 crypt44[] = { + 0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32, + 0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC, + 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, + 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2 + }; + /* RFC 3394 - Test vector 4.5 */ + u8 kek45[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + u8 plain45[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + }; + u8 crypt45[] = { + 0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F, + 0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4, + 0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95, + 0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1, + }; + /* RFC 3394 - Test vector 4.6 */ + u8 kek46[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + u8 plain46[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F + }; + u8 crypt46[] = { + 0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4, + 0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26, + 0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26, + 0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B, + 0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21 + }; + u8 result[40]; + + wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.1"); + if (aes_wrap(kek41, sizeof(kek41), sizeof(plain41) / 8, plain41, + result)) { + wpa_printf(MSG_ERROR, "AES-WRAP-128 reported failure"); + ret++; + } + if (os_memcmp(result, crypt41, sizeof(crypt41)) != 0) { + wpa_printf(MSG_ERROR, "AES-WRAP-128 failed"); + ret++; + } + if (aes_unwrap(kek41, sizeof(kek41), sizeof(plain41) / 8, crypt41, + result)) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-128 reported failure"); + ret++; + } + if (os_memcmp(result, plain41, sizeof(plain41)) != 0) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-128 failed"); + ret++; + } + + wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2"); + if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42, + result)) { + wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure"); + ret++; + } + if (os_memcmp(result, crypt42, sizeof(crypt42)) != 0) { + wpa_printf(MSG_ERROR, "AES-WRAP-192 failed"); + ret++; + } + if (aes_unwrap(kek42, sizeof(kek42), sizeof(plain42) / 8, crypt42, + result)) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure"); + ret++; + } + if (os_memcmp(result, plain42, sizeof(plain42)) != 0) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); + ret++; + } + + wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3"); + if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43, + result)) { + wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure"); + ret++; + } + if (os_memcmp(result, crypt43, sizeof(crypt43)) != 0) { + wpa_printf(MSG_ERROR, "AES-WRAP-256 failed"); + ret++; + } + if (aes_unwrap(kek43, sizeof(kek43), sizeof(plain43) / 8, crypt43, + result)) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure"); + ret++; + } + if (os_memcmp(result, plain43, sizeof(plain43)) != 0) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed"); + ret++; + } + + wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4"); + if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44, + result)) { + wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure"); + ret++; + } + if (os_memcmp(result, crypt44, sizeof(crypt44)) != 0) { + wpa_printf(MSG_ERROR, "AES-WRAP-192 failed"); + ret++; + } + if (aes_unwrap(kek44, sizeof(kek44), sizeof(plain44) / 8, crypt44, + result)) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure"); + ret++; + } + if (os_memcmp(result, plain44, sizeof(plain44)) != 0) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed"); + ret++; + } + + wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5"); + if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45, + result)) { + wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure"); + ret++; + } + if (os_memcmp(result, crypt45, sizeof(crypt45)) != 0) { + wpa_printf(MSG_ERROR, "AES-WRAP-256 failed"); + ret++; + } + if (aes_unwrap(kek45, sizeof(kek45), sizeof(plain45) / 8, crypt45, + result)) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure"); + ret++; + } + if (os_memcmp(result, plain45, sizeof(plain45)) != 0) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed"); + ret++; + } + + wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.6"); + if (aes_wrap(kek46, sizeof(kek46), sizeof(plain46) / 8, plain46, + result)) { + wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure"); + ret++; + } + if (os_memcmp(result, crypt46, sizeof(crypt46)) != 0) { + wpa_printf(MSG_ERROR, "AES-WRAP-256 failed"); + ret++; + } + if (aes_unwrap(kek46, sizeof(kek46), sizeof(plain46) / 8, crypt46, + result)) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure"); + ret++; + } + if (os_memcmp(result, plain46, sizeof(plain46)) != 0) { + wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed"); + ret++; + } + + if (!ret) + wpa_printf(MSG_INFO, "AES key wrap/unwrap test cases passed"); + + return ret; +} + + +static int test_md5(void) +{ + struct { + char *data; + char *hash; + } tests[] = { + { + "", + "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" + "\xe9\x80\x09\x98\xec\xf8\x42\x7e" + }, + { + "a", + "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8" + "\x31\xc3\x99\xe2\x69\x77\x26\x61" + }, + { + "abc", + "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" + "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72" + }, + { + "message digest", + "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d" + "\x52\x5a\x2f\x31\xaa\xf1\x61\xd0" + }, + { + "abcdefghijklmnopqrstuvwxyz", + "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00" + "\x7d\xfb\x49\x6c\xca\x67\xe1\x3b" + }, + { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789", + "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" + "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f" + }, + { + "12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890", + "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55" + "\xac\x49\xda\x2e\x21\x07\xb6\x7a" + } + }; + unsigned int i; + u8 hash[16]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + wpa_printf(MSG_INFO, "MD5 test case %d", i); + + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + if (md5_vector(1, addr, len, hash) < 0 || + os_memcmp(hash, tests[i].hash, 16) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + if (len[0]) { + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + addr[1] = (u8 *) tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + if (md5_vector(1, addr, len, hash) < 0 || + os_memcmp(hash, tests[i].hash, 16) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + } + } + + if (!errors) + wpa_printf(MSG_INFO, "MD5 test cases passed"); + + return errors; +} + + +static int test_eap_fast(void) +{ +#ifdef EAP_FAST + /* RFC 4851, Appendix B.1 */ + const u8 pac_key[] = { + 0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09, + 0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B, + 0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA, + 0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14 + }; + const u8 seed[] = { + 0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A, + 0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3, + 0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93, + 0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A, + 0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A, + 0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F, + 0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A, + 0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00 + }; + const u8 master_secret[] = { + 0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02, + 0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64, + 0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77, + 0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29, + 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, + 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 + }; + const u8 key_block[] = { + 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, + 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, + 0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B, + 0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57, + 0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70, + 0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB, + 0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF, + 0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44, + 0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29, + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 sks[] = { + 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, + 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, + 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, + 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, + 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 + }; + const u8 isk[] = { + 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 + }; + const u8 imck[] = { + 0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9, + 0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80, + 0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96, + 0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1, + 0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5, + 0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9, + 0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8, + 0x15, 0xEC, 0x57, 0x7B + }; + const u8 msk[] = { + 0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED, + 0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33, + 0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51, + 0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9, + 0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A, + 0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49, + 0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59, + 0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3 + }; + const u8 emsk[] = { + 0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B, + 0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55, + 0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D, + 0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9, + 0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54, + 0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A, + 0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44, + 0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9 + }; + /* RFC 4851, Appendix B.2 */ + u8 tlv[] = { + 0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00, + 0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8, + 0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14, + 0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62, + 0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58, + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + const u8 compound_mac[] = { + 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, + 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, + 0x05, 0xC5, 0x5B, 0xB7 + }; + u8 buf[512]; + const u8 *simck, *cmk; + int errors = 0; + + wpa_printf(MSG_INFO, "EAP-FAST test cases"); + + wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / master_secret"); + if (sha1_t_prf(pac_key, sizeof(pac_key), + "PAC to master secret label hash", + seed, sizeof(seed), buf, sizeof(master_secret)) < 0 || + os_memcmp(master_secret, buf, sizeof(master_secret)) != 0) { + wpa_printf(MSG_INFO, "T-PRF test - FAILED!"); + errors++; + } + + wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block"); + if (tls_prf_sha1_md5(master_secret, sizeof(master_secret), + "key expansion", seed, sizeof(seed), + buf, sizeof(key_block)) || + os_memcmp(key_block, buf, sizeof(key_block)) != 0) { + wpa_printf(MSG_INFO, "PRF test - FAILED!"); + errors++; + } + + wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK"); + if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", + isk, sizeof(isk), buf, sizeof(imck)) < 0 || + os_memcmp(imck, buf, sizeof(imck)) != 0) { + wpa_printf(MSG_INFO, "T-PRF test - FAILED!"); + errors++; + } + + simck = imck; + cmk = imck + 40; + + wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / MSK"); + if (sha1_t_prf(simck, 40, "Session Key Generating Function", + (u8 *) "", 0, buf, sizeof(msk)) < 0 || + os_memcmp(msk, buf, sizeof(msk)) != 0) { + wpa_printf(MSG_INFO, "T-PRF test - FAILED!"); + errors++; + } + + wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / EMSK"); + if (sha1_t_prf(simck, 40, "Extended Session Key Generating Function", + (u8 *) "", 0, buf, sizeof(msk)) < 0 || + os_memcmp(emsk, buf, sizeof(emsk)) != 0) { + wpa_printf(MSG_INFO, "T-PRF test - FAILED!"); + errors++; + } + + wpa_printf(MSG_INFO, "- Compound MAC test case"); + os_memset(tlv + sizeof(tlv) - 20, 0, 20); + if (hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20) < 0 || + os_memcmp(tlv + sizeof(tlv) - 20, compound_mac, + sizeof(compound_mac)) != 0) { + wpa_printf(MSG_INFO, "Compound MAC test - FAILED!"); + errors++; + } + + return errors; +#else /* EAP_FAST */ + return 0; +#endif /* EAP_FAST */ +} + + +static u8 key0[] = +{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b +}; +static u8 data0[] = "Hi There"; +static u8 prf0[] = +{ + 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, + 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, + 0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06, + 0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee, + 0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88, + 0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb, + 0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e, + 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a +}; + +static u8 key1[] = "Jefe"; +static u8 data1[] = "what do ya want for nothing?"; +static u8 prf1[] = +{ + 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, + 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, + 0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58, + 0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09, + 0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa, + 0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02, + 0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7, + 0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc +}; + + +static u8 key2[] = +{ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa +}; +static u8 data2[] = +{ + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd +}; +static u8 prf2[] = +{ + 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, + 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, + 0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1, + 0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce, + 0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc, + 0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae, + 0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6, + 0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07 +}; + + +struct passphrase_test { + char *passphrase; + char *ssid; + char psk[32]; +}; + +static struct passphrase_test passphrase_tests[] = +{ + { + "password", + "IEEE", + { + 0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef, + 0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90, + 0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2, + 0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e + } + }, + { + "ThisIsAPassword", + "ThisIsASSID", + { + 0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6, + 0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3, + 0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08, + 0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf + } + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", + { + 0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83, + 0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c, + 0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48, + 0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62 + } + }, +}; + +#define NUM_PASSPHRASE_TESTS ARRAY_SIZE(passphrase_tests) + + +struct rfc6070_test { + char *p; + char *s; + int c; + char dk[32]; + size_t dk_len; +}; + +static struct rfc6070_test rfc6070_tests[] = +{ + { + "password", + "salt", + 1, + { + 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, + 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, + 0x2f, 0xe0, 0x37, 0xa6 + }, + 20 + }, + { + "password", + "salt", + 2, + { + 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, + 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, + 0xd8, 0xde, 0x89, 0x57 + }, + 20 + }, + { + "password", + "salt", + 4096, + { + 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, + 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, + 0x65, 0xa4, 0x29, 0xc1 + }, + 20 + }, +#if 0 /* This takes quite long to derive.. */ + { + "password", + "salt", + 16777216, + { + 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, + 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, + 0x26, 0x34, 0xe9, 0x84 + }, + 20 + }, +#endif + { + "passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + { + 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, + 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, + 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, + 0x38 + }, + 25 + }, +#if 0 /* \0 not currently supported in passphrase parameters.. */ + { + "pass\0word", + "sa\0lt", + 4096, + { + 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, + 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 + }, + 16 + }, +#endif +}; + +#define NUM_RFC6070_TESTS ARRAY_SIZE(rfc6070_tests) + + +static int test_sha1(void) +{ + u8 res[512]; + int ret = 0; + unsigned int i; + + wpa_printf(MSG_INFO, "PRF-SHA1 test cases:"); + + if (sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1, + res, sizeof(prf0)) == 0 && + os_memcmp(res, prf0, sizeof(prf0)) == 0) + wpa_printf(MSG_INFO, "Test case 0 - OK"); + else { + wpa_printf(MSG_INFO, "Test case 0 - FAILED!"); + ret++; + } + + if (sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1, + res, sizeof(prf1)) == 0 && + os_memcmp(res, prf1, sizeof(prf1)) == 0) + wpa_printf(MSG_INFO, "Test case 1 - OK"); + else { + wpa_printf(MSG_INFO, "Test case 1 - FAILED!"); + ret++; + } + + if (sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2), + res, sizeof(prf2)) == 0 && + os_memcmp(res, prf2, sizeof(prf2)) == 0) + wpa_printf(MSG_INFO, "Test case 2 - OK"); + else { + wpa_printf(MSG_INFO, "Test case 2 - FAILED!"); + ret++; + } + + ret += test_eap_fast(); + + wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:"); + for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { + u8 psk[32]; + struct passphrase_test *test = &passphrase_tests[i]; + + if (pbkdf2_sha1(test->passphrase, + (const u8 *) test->ssid, strlen(test->ssid), + 4096, psk, 32) == 0 && + os_memcmp(psk, test->psk, 32) == 0) + wpa_printf(MSG_INFO, "Test case %d - OK", i); + else { + wpa_printf(MSG_INFO, "Test case %d - FAILED!", i); + ret++; + } + } + + wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):"); + for (i = 0; i < NUM_RFC6070_TESTS; i++) { + u8 dk[25]; + struct rfc6070_test *test = &rfc6070_tests[i]; + + if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s), + test->c, dk, test->dk_len) == 0 && + os_memcmp(dk, test->dk, test->dk_len) == 0) + wpa_printf(MSG_INFO, "Test case %d - OK", i); + else { + wpa_printf(MSG_INFO, "Test case %d - FAILED!", i); + ret++; + } + } + + if (!ret) + wpa_printf(MSG_INFO, "SHA1 test cases passed"); + return ret; +} + + +struct { + char *data; + u8 hash[32]; +} tests[] = { + { + "abc", + { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + } + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { + 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 + } + } +}; + +struct hmac_test { + u8 key[80]; + size_t key_len; + u8 data[128]; + size_t data_len; + u8 hash[32]; +} hmac_tests[] = { + /* draft-ietf-ipsec-ciph-sha-256-01.txt */ + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 + }, + 32, + "abc", 3, + { + 0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a, + 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a, + 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66, + 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81 + } + }, + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 + }, + 32, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56, + { + 0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08, + 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae, + 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49, + 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30 + } + }, + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 + }, + 32, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 112, + { + 0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34, + 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab, + 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5, + 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3 + } + }, + { + { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }, + 32, + "Hi There", + 8, + { + 0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6, + 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5, + 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, + 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7 + } + }, + { + "Jefe", + 4, + "what do ya want for nothing?", + 28, + { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, + 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, + 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, + 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 32, + { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd + }, + 50, + { + 0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea, + 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62, + 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc, + 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0 + } + }, + { + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25 + }, + 37, + { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }, + 50, + { + 0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74, + 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55, + 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85, + 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17 + } + }, + { + { + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c + }, + 32, + "Test With Truncation", + 20, + { + 0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b, + 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17, + 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27, + 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42 + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54, + { + 0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09, + 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb, + 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e, + 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 80, + "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + 73, + { + 0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3, + 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8, + 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc, + 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6 + } + } +}; + + +static int test_sha256(void) +{ + unsigned int i; + u8 hash[32]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1); + + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + sha256_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 32) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + if (len[0]) { + addr[0] = (u8 *) tests[i].data; + len[0] = 1; + addr[1] = (u8 *) tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + sha256_vector(2, addr, len, hash); + if (memcmp(hash, tests[i].hash, 32) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + } + } + + for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { + struct hmac_test *t = &hmac_tests[i]; + + wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1); + + if (hmac_sha256(t->key, t->key_len, t->data, t->data_len, + hash) < 0 || + os_memcmp(hash, t->hash, 32) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + addr[0] = t->data; + len[0] = t->data_len; + if (hmac_sha256_vector(t->key, t->key_len, 1, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash, 32) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + if (len[0]) { + addr[0] = t->data; + len[0] = 1; + addr[1] = t->data + 1; + len[1] = t->data_len - 1; + if (hmac_sha256_vector(t->key, t->key_len, 2, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash, 32) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + } + } + + wpa_printf(MSG_INFO, "Test IEEE 802.11r KDF"); + sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4, + hash, sizeof(hash)); + /* TODO: add proper test case for this */ + + if (!errors) + wpa_printf(MSG_INFO, "SHA256 test cases passed"); + return errors; +} + + +static int test_ms_funcs(void) +{ + /* Test vector from RFC2759 example */ + char *username = "User"; + char *password = "clientPass"; + u8 auth_challenge[] = { + 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, + 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 + }; + u8 peer_challenge[] = { + 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, + 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E + }; + u8 password_hash[] = { + 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, + 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE + }; + u8 nt_response[] = { + 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, + 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, + 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF + }; + u8 password_hash_hash[] = { + 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, + 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F + }; + u8 authenticator_response[] = { + 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, + 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, + 0x93, 0x2C, 0xDA, 0x56 + }; + u8 master_key[] = { + 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, + 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 + }; + u8 send_start_key[] = { + 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, + 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB + }; + u8 buf[32]; + int errors = 0; + + if (nt_password_hash((u8 *) password, os_strlen(password), buf) || + os_memcmp(password_hash, buf, sizeof(password_hash)) != 0) { + wpa_printf(MSG_ERROR, "nt_password_hash failed"); + errors++; + } + + if (generate_nt_response(auth_challenge, peer_challenge, + (u8 *) username, os_strlen(username), + (u8 *) password, os_strlen(password), buf) || + os_memcmp(nt_response, buf, sizeof(nt_response)) != 0) { + wpa_printf(MSG_ERROR, "generate_nt_response failed"); + errors++; + } + + if (hash_nt_password_hash(password_hash, buf) || + os_memcmp(password_hash_hash, buf, + sizeof(password_hash_hash)) != 0) { + wpa_printf(MSG_ERROR, "hash_nt_password_hash failed"); + errors++; + } + + if (generate_authenticator_response((u8 *) password, + os_strlen(password), + peer_challenge, auth_challenge, + (u8 *) username, + os_strlen(username), + nt_response, buf) || + os_memcmp(authenticator_response, buf, + sizeof(authenticator_response)) != 0) { + wpa_printf(MSG_ERROR, "generate_authenticator_response failed"); + errors++; + } + + if (get_master_key(password_hash_hash, nt_response, buf) || + os_memcmp(master_key, buf, sizeof(master_key)) != 0) { + wpa_printf(MSG_ERROR, "get_master_key failed"); + errors++; + } + + if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key), + 1, 1) || + os_memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { + wpa_printf(MSG_ERROR, "get_asymetric_start_key failed"); + errors++; + } + + if (errors) + wpa_printf(MSG_ERROR, "ms_funcs: %d errors", errors); + else + wpa_printf(MSG_INFO, "ms_funcs test cases passed"); + + return errors; +} + + +int crypto_module_tests(void) +{ + int ret = 0; + + wpa_printf(MSG_INFO, "crypto module tests"); + if (test_siv() || + test_omac1() || + test_eax() || + test_cbc() || + test_ecb() || + test_key_wrap() || + test_md5() || + test_sha1() || + test_sha256() || + test_ms_funcs()) + ret = -1; + + return ret; +} diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c deleted file mode 100644 index acd0a55281c0..000000000000 --- a/src/crypto/crypto_nss.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Crypto wrapper functions for NSS - * Copyright (c) 2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "crypto.h" - - -static int nss_hash(HASH_HashType type, unsigned int max_res_len, - size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) -{ - HASHContext *ctx; - size_t i; - unsigned int reslen; - - ctx = HASH_Create(type); - if (ctx == NULL) - return -1; - - HASH_Begin(ctx); - for (i = 0; i < num_elem; i++) - HASH_Update(ctx, addr[i], len[i]); - HASH_End(ctx, mac, &reslen, max_res_len); - HASH_Destroy(ctx); - - return 0; -} - - -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) -{ - PK11Context *ctx = NULL; - PK11SlotInfo *slot; - SECItem *param = NULL; - PK11SymKey *symkey = NULL; - SECItem item; - int olen; - u8 pkey[8], next, tmp; - int i; - - /* 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; - - slot = PK11_GetBestSlot(CKM_DES_ECB, NULL); - if (slot == NULL) { - wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed"); - goto out; - } - - item.type = siBuffer; - item.data = pkey; - item.len = 8; - symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive, - CKA_ENCRYPT, &item, NULL); - if (symkey == NULL) { - wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed"); - goto out; - } - - param = PK11_GenerateNewParam(CKM_DES_ECB, symkey); - if (param == NULL) { - wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed"); - goto out; - } - - ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT, - symkey, param); - if (ctx == NULL) { - wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey(" - "CKM_DES_ECB) failed"); - goto out; - } - - if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) != - SECSuccess) { - wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed"); - goto out; - } - -out: - if (ctx) - PK11_DestroyContext(ctx, PR_TRUE); - if (symkey) - PK11_FreeSymKey(symkey); - if (param) - SECITEM_FreeItem(param, PR_TRUE); -} - - -int rc4_skip(const u8 *key, size_t keylen, size_t skip, - u8 *data, size_t data_len) -{ - return -1; -} - - -int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac); -} - - -int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac); -} - - -int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) -{ - return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac); -} - - -void * aes_encrypt_init(const u8 *key, size_t len) -{ - return NULL; -} - - -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) -{ - return NULL; -} - - -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) -{ -} - - -void aes_decrypt_deinit(void *ctx) -{ -} - - -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) -{ - return -1; -} - - -struct crypto_cipher { -}; - - -struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, - const u8 *iv, const u8 *key, - size_t key_len) -{ - return NULL; -} - - -int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, - u8 *crypt, size_t len) -{ - return -1; -} - - -int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, - u8 *plain, size_t len) -{ - return -1; -} - - -void crypto_cipher_deinit(struct crypto_cipher *ctx) -{ -} diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 711e312d0fb0..f158ef43a645 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2012, Jouni Malinen + * Wrapper functions for OpenSSL libcrypto + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,23 +19,21 @@ #ifdef CONFIG_OPENSSL_CMAC #include #endif /* CONFIG_OPENSSL_CMAC */ +#ifdef CONFIG_ECC +#include +#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" -#if OPENSSL_VERSION_NUMBER < 0x00907000 -#define DES_key_schedule des_key_schedule -#define DES_cblock des_cblock -#define DES_set_key(key, schedule) des_set_key((key), *(schedule)) -#define DES_ecb_encrypt(input, output, ks, enc) \ - des_ecb_encrypt((input), (output), *(ks), (enc)) -#endif /* openssl < 0.9.7 */ - static BIGNUM * get_group5_prime(void) { -#if OPENSSL_VERSION_NUMBER < 0x00908000 +#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, @@ -55,20 +53,11 @@ static BIGNUM * get_group5_prime(void) 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 < 0.9.8 */ +#else /* OPENSSL_IS_BORINGSSL */ return get_rfc3526_prime_1536(NULL); -#endif /* openssl < 0.9.8 */ +#endif /* OPENSSL_IS_BORINGSSL */ } -#if OPENSSL_VERSION_NUMBER < 0x00908000 -#ifndef OPENSSL_NO_SHA256 -#ifndef OPENSSL_FIPS -#define NO_SHA256_WRAPPER -#endif -#endif - -#endif /* openssl < 0.9.8 */ - #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif @@ -125,7 +114,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } pkey[i] = next | 1; - DES_set_key(&pkey, &ks); + DES_set_key((DES_cblock *) &pkey, &ks); DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); } @@ -194,8 +183,10 @@ 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(); } @@ -251,7 +242,7 @@ void aes_encrypt_deinit(void *ctx) "in AES encrypt", len); } EVP_CIPHER_CTX_cleanup(c); - os_free(c); + bin_clear_free(c, sizeof(*c)); } @@ -302,7 +293,34 @@ void aes_decrypt_deinit(void *ctx) "in AES decrypt", len); } EVP_CIPHER_CTX_cleanup(c); - os_free(ctx); + bin_clear_free(c, sizeof(*c)); +} + + +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; } @@ -335,10 +353,10 @@ int crypto_mod_exp(const u8 *base, size_t base_len, ret = 0; error: - BN_free(bn_base); - BN_free(bn_exp); - BN_free(bn_modulus); - BN_free(bn_result); + 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; } @@ -373,9 +391,11 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, 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; @@ -498,8 +518,8 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) return dh; err: - wpabuf_free(pubkey); - wpabuf_free(privkey); + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); DH_free(dh); return NULL; } @@ -566,13 +586,13 @@ struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, if (keylen < 0) goto err; wpabuf_put(res, keylen); - BN_free(pub_key); + BN_clear_free(pub_key); return res; err: - BN_free(pub_key); - wpabuf_free(res); + BN_clear_free(pub_key); + wpabuf_clear_free(res); return NULL; } @@ -629,7 +649,7 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, 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) { - os_free(ctx); + bin_clear_free(ctx, sizeof(*ctx)); return NULL; } #endif /* openssl < 0.9.9 */ @@ -655,7 +675,7 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) return -2; if (mac == NULL || len == NULL) { - os_free(ctx); + bin_clear_free(ctx, sizeof(*ctx)); return 0; } @@ -667,7 +687,7 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) res = HMAC_Final(&ctx->ctx, mac, &mdlen); #endif /* openssl < 0.9.9 */ HMAC_CTX_cleanup(&ctx->ctx); - os_free(ctx); + bin_clear_free(ctx, sizeof(*ctx)); if (res == 1) { *len = mdlen; @@ -678,43 +698,26 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } -int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, - int iterations, u8 *buf, size_t buflen) -{ -#if OPENSSL_VERSION_NUMBER < 0x00908000 - if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), - (unsigned char *) ssid, - ssid_len, 4096, buflen, buf) != 1) - return -1; -#else /* openssl < 0.9.8 */ - if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, - ssid_len, 4096, buflen, buf) != 1) - return -1; -#endif /* openssl < 0.9.8 */ - 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) +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; - unsigned int mdlen; int res; HMAC_CTX_init(&ctx); #if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL); + HMAC_Init_ex(&ctx, key, key_len, type, NULL); #else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) + 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]); - mdlen = 20; #if OPENSSL_VERSION_NUMBER < 0x00909000 HMAC_Final(&ctx, mac, &mdlen); res = 1; @@ -727,6 +730,43 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, } +#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) { @@ -739,42 +779,37 @@ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - HMAC_CTX ctx; - size_t i; - unsigned int mdlen; - int res; + return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr, + len, mac, 32); +} - HMAC_CTX_init(&ctx); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1) - return -1; -#endif /* openssl < 0.9.9 */ - for (i = 0; i < num_elem; i++) - HMAC_Update(&ctx, addr[i], len[i]); +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); +} - mdlen = 32; -#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); +#endif /* CONFIG_SHA256 */ - return res == 1 ? 0 : -1; + +#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_sha256(const u8 *key, size_t key_len, const u8 *data, +int hmac_sha384(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); + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); } -#endif /* CONFIG_SHA256 */ +#endif /* CONFIG_SHA384 */ int crypto_get_random(void *buf, size_t len) @@ -786,8 +821,8 @@ int crypto_get_random(void *buf, size_t len) #ifdef CONFIG_OPENSSL_CMAC -int omac1_aes_128_vector(const u8 *key, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +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; @@ -797,8 +832,15 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem, if (ctx == NULL) return -1; - if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) + 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; @@ -813,8 +855,425 @@ fail: } +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); +} + + +#ifdef CONFIG_ECC + +struct crypto_ec { + EC_GROUP *group; + BN_CTX *bnctx; + BIGNUM *prime; + BIGNUM *order; +}; + +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; + 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(); + if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || + e->order == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, 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->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; +} + + +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); +} + +#endif /* CONFIG_ECC */ diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index f757b6b54e8f..d3b263196e2d 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -35,6 +35,20 @@ static const u8 dh_group1_prime[96] = { 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group1_order[96] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 4306, B.2. Group 2 - 1024 Bit MODP * Generator: 2 @@ -59,6 +73,24 @@ static const u8 dh_group2_prime[128] = { 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group2_order[128] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; #endif /* ALL_DH_GROUPS */ @@ -93,6 +125,32 @@ static const u8 dh_group5_prime[192] = { 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group5_order[192] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; #ifdef ALL_DH_GROUPS @@ -135,6 +193,40 @@ static const u8 dh_group14_prime[256] = { 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group14_order[256] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 4. Group 15 - 3072 Bit MODP * Generator: 2 @@ -191,6 +283,56 @@ static const u8 dh_group15_prime[384] = { 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group15_order[384] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 5. Group 16 - 4096 Bit MODP * Generator: 2 @@ -263,6 +405,72 @@ static const u8 dh_group16_prime[512] = { 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group16_order[512] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 6. Group 17 - 6144 Bit MODP * Generator: 2 @@ -367,6 +575,104 @@ static const u8 dh_group17_prime[768] = { 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group17_order[768] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, + 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, + 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, + 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, + 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, + 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, + 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, + 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, + 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, + 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, + 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, + 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, + 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, + 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, + 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, + 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, + 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, + 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, + 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, + 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, + 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, + 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, + 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, + 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, + 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, + 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, + 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, + 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, + 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, + 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, + 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, + 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, + 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 7. Group 18 - 8192 Bit MODP * Generator: 2 @@ -503,29 +809,367 @@ static const u8 dh_group18_prime[1024] = { 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group18_order[1024] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, + 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, + 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, + 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, + 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, + 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, + 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, + 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, + 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, + 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, + 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, + 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, + 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, + 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, + 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, + 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, + 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, + 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, + 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, + 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, + 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, + 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, + 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, + 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, + 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, + 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, + 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, + 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, + 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, + 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, + 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, + 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, + 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC, + 0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2, + 0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6, + 0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD, + 0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80, + 0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6, + 0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33, + 0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4, + 0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC, + 0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96, + 0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C, + 0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03, + 0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63, + 0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35, + 0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE, + 0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4, + 0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D, + 0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53, + 0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16, + 0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B, + 0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0, + 0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E, + 0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50, + 0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9, + 0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34, + 0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9, + 0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B, + 0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15, + 0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23, + 0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5, + 0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F, + 0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38, + 0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* + * RFC 5114, 2.1. + * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup + */ +static const u8 dh_group22_generator[] = { + 0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12, + 0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05, + 0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F, + 0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31, + 0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B, + 0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13, + 0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A, + 0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4, + 0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1, + 0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76, + 0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53, + 0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A, + 0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3, + 0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8, + 0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24, + 0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5 +}; +static const u8 dh_group22_prime[] = { + 0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D, + 0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC, + 0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6, + 0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61, + 0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18, + 0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0, + 0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23, + 0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF, + 0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70, + 0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72, + 0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38, + 0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0, + 0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD, + 0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65, + 0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08, + 0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71 +}; +static const u8 dh_group22_order[] = { + 0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27, + 0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D, + 0x49, 0x46, 0x23, 0x53 +}; + +/* + * RFC 5114, 2.2. + * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup + */ +static const u8 dh_group23_generator[] = { + 0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3, + 0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50, + 0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF, + 0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3, + 0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0, + 0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA, + 0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52, + 0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52, + 0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7, + 0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A, + 0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B, + 0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A, + 0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4, + 0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD, + 0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE, + 0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1, + 0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56, + 0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF, + 0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B, + 0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC, + 0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB, + 0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD, + 0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF, + 0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81, + 0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD, + 0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91, + 0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69, + 0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD, + 0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C, + 0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79, + 0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3, + 0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA +}; +static const u8 dh_group23_prime[] = { + 0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0, + 0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F, + 0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1, + 0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75, + 0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB, + 0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15, + 0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E, + 0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6, + 0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12, + 0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8, + 0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B, + 0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07, + 0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6, + 0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE, + 0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08, + 0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36, + 0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB, + 0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30, + 0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC, + 0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74, + 0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D, + 0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87, + 0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4, + 0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8, + 0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9, + 0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B, + 0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63, + 0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29, + 0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9, + 0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71, + 0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C, + 0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F +}; +static const u8 dh_group23_order[] = { + 0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE, + 0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A, + 0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99, + 0xB3, 0x63, 0x71, 0xEB +}; + +/* + * RFC 5114, 2.3. + * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup + */ +static const u8 dh_group24_generator[] = { + 0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B, + 0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48, + 0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54, + 0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25, + 0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F, + 0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55, + 0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1, + 0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62, + 0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18, + 0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65, + 0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2, + 0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B, + 0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62, + 0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38, + 0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83, + 0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93, + 0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1, + 0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55, + 0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80, + 0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A, + 0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14, + 0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9, + 0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99, + 0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15, + 0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37, + 0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52, + 0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6, + 0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3, + 0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8, + 0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51, + 0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82, + 0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59 +}; +static const u8 dh_group24_prime[] = { + 0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C, + 0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99, + 0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2, + 0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00, + 0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4, + 0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30, + 0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA, + 0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C, + 0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD, + 0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED, + 0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0, + 0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B, + 0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88, + 0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8, + 0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C, + 0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76, + 0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90, + 0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E, + 0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB, + 0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E, + 0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9, + 0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25, + 0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6, + 0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26, + 0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56, + 0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21, + 0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3, + 0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03, + 0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12, + 0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F, + 0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA, + 0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97 +}; +static const u8 dh_group24_order[] = { + 0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97, + 0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2, + 0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B, + 0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3 +}; #endif /* ALL_DH_GROUPS */ -#define DH_GROUP(id) \ +#define DH_GROUP(id,safe) \ { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ -dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) } +dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ +dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } static struct dh_group dh_groups[] = { - DH_GROUP(5), + DH_GROUP(5, 1), #ifdef ALL_DH_GROUPS - DH_GROUP(1), - DH_GROUP(2), - DH_GROUP(14), - DH_GROUP(15), - DH_GROUP(16), - DH_GROUP(17), - DH_GROUP(18) + DH_GROUP(1, 1), + DH_GROUP(2, 1), + DH_GROUP(14, 1), + DH_GROUP(15, 1), + DH_GROUP(16, 1), + DH_GROUP(17, 1), + DH_GROUP(18, 1), + DH_GROUP(22, 0), + DH_GROUP(23, 0), + DH_GROUP(24, 0) #endif /* ALL_DH_GROUPS */ }; -#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0])) +#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups) const struct dh_group * dh_groups_get(int id) @@ -554,14 +1198,14 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (dh == NULL) return NULL; - wpabuf_free(*priv); + wpabuf_clear_free(*priv); *priv = wpabuf_alloc(dh->prime_len); if (*priv == NULL) return NULL; if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) { - wpabuf_free(*priv); + wpabuf_clear_free(*priv); *priv = NULL; return NULL; } @@ -580,7 +1224,7 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) wpabuf_head(*priv), wpabuf_len(*priv), dh->prime, dh->prime_len, wpabuf_mhead(pv), &pv_len) < 0) { - wpabuf_free(pv); + wpabuf_clear_free(pv); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; } @@ -616,7 +1260,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, wpabuf_head(own_private), wpabuf_len(own_private), dh->prime, dh->prime_len, wpabuf_mhead(shared), &shared_len) < 0) { - wpabuf_free(shared); + wpabuf_clear_free(shared); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; } diff --git a/src/crypto/dh_groups.h b/src/crypto/dh_groups.h index 225f0067a17f..d0e74b9206a8 100644 --- a/src/crypto/dh_groups.h +++ b/src/crypto/dh_groups.h @@ -15,6 +15,9 @@ struct dh_group { size_t generator_len; const u8 *prime; size_t prime_len; + const u8 *order; + size_t order_len; + unsigned int safe_prime:1; }; const struct dh_group * dh_groups_get(int id); diff --git a/src/crypto/fips_prf_cryptoapi.c b/src/crypto/fips_prf_cryptoapi.c deleted file mode 100644 index dca93a3d3366..000000000000 --- a/src/crypto/fips_prf_cryptoapi.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * FIPS 186-2 PRF for Microsoft CryptoAPI - * Copyright (c) 2009, Jouni Malinen - * - * 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" - - -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) -{ - /* FIX: how to do this with CryptoAPI? */ - return -1; -} diff --git a/src/crypto/fips_prf_gnutls.c b/src/crypto/fips_prf_gnutls.c deleted file mode 100644 index 947e6f641462..000000000000 --- a/src/crypto/fips_prf_gnutls.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * FIPS 186-2 PRF for libgcrypt - * Copyright (c) 2004-2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include - -#include "common.h" -#include "crypto.h" - - -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) -{ - /* FIX: how to do this with libgcrypt? */ - return -1; -} diff --git a/src/crypto/fips_prf_nss.c b/src/crypto/fips_prf_nss.c deleted file mode 100644 index 2c962f4f1301..000000000000 --- a/src/crypto/fips_prf_nss.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * FIPS 186-2 PRF for NSS - * Copyright (c) 2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include - -#include "common.h" -#include "crypto.h" - - -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) -{ - return -1; -} diff --git a/src/crypto/md5.c b/src/crypto/md5.c index db2b8cc316ba..f64dfd3d43cd 100644 --- a/src/crypto/md5.c +++ b/src/crypto/md5.c @@ -30,6 +30,7 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, u8 tk[16]; const u8 *_addr[6]; size_t i, _len[6]; + int res; if (num_elem > 5) { /* @@ -85,7 +86,10 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = MD5_MAC_LEN; - return md5_vector(2, _addr, _len, mac); + res = md5_vector(2, _addr, _len, mac); + os_memset(k_pad, 0, sizeof(k_pad)); + os_memset(tk, 0, sizeof(tk)); + return res; } diff --git a/src/crypto/milenage.c b/src/crypto/milenage.c index a7f9c6a286a8..6edea57e69e5 100644 --- a/src/crypto/milenage.c +++ b/src/crypto/milenage.c @@ -217,7 +217,7 @@ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, for (i = 0; i < 6; i++) sqn[i] = auts[i] ^ ak[i]; if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || - memcmp(mac_s, auts + 6, 8) != 0) + os_memcmp_const(mac_s, auts + 6, 8) != 0) return -1; return 0; } @@ -312,7 +312,7 @@ int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); - if (os_memcmp(mac_a, autn + 8, 8) != 0) { + if (os_memcmp_const(mac_a, autn + 8, 8) != 0) { wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", autn + 8, 8); diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index b2bbab2b5c32..49a5c1c245d6 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -58,6 +58,7 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, WPA_PUT_LE16(ucs2_buffer + j, ((c & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); + j += 2; } } } diff --git a/src/crypto/random.c b/src/crypto/random.c index 053740e9bfea..bc758aa57232 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -232,12 +232,8 @@ int random_pool_ready(void) */ fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (fd < 0) { -#ifndef CONFIG_NO_STDOUT_DEBUG - int error = errno; - perror("open(/dev/random)"); wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(error)); -#endif /* CONFIG_NO_STDOUT_DEBUG */ + strerror(errno)); return -1; } @@ -417,12 +413,8 @@ void random_init(const char *entropy_file) random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { -#ifndef CONFIG_NO_STDOUT_DEBUG - int error = errno; - perror("open(/dev/random)"); wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(error)); -#endif /* CONFIG_NO_STDOUT_DEBUG */ + strerror(errno)); return; } wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c index 10bf153ca30e..24bc3ffe1759 100644 --- a/src/crypto/sha1-internal.c +++ b/src/crypto/sha1-internal.c @@ -19,6 +19,7 @@ typedef struct SHA1Context SHA1_CTX; void SHA1Transform(u32 state[5], const unsigned char buffer[64]); +#ifdef CONFIG_CRYPTO_INTERNAL /** * sha1_vector - SHA-1 hash for data vector * @num_elem: Number of elements in the data vector @@ -38,6 +39,7 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) SHA1Final(mac, &ctx); return 0; } +#endif /* CONFIG_CRYPTO_INTERNAL */ /* ===== start - public domain SHA1 implementation ===== */ diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c index 90b9e74b745b..4b2d1373067f 100644 --- a/src/crypto/sha1-prf.c +++ b/src/crypto/sha1-prf.c @@ -61,6 +61,7 @@ int sha1_prf(const u8 *key, size_t key_len, const char *label, } counter++; } + os_memset(hash, 0, sizeof(hash)); return 0; } diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c index d48c77d75c5f..8fce139408f1 100644 --- a/src/crypto/sha1.c +++ b/src/crypto/sha1.c @@ -30,6 +30,7 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, unsigned char tk[20]; const u8 *_addr[6]; size_t _len[6], i; + int ret; if (num_elem > 5) { /* @@ -84,7 +85,9 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = SHA1_MAC_LEN; - return sha1_vector(2, _addr, _len, mac); + ret = sha1_vector(2, _addr, _len, mac); + os_memset(k_pad, 0, sizeof(k_pad)); + return ret; } diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c new file mode 100644 index 000000000000..d8a1beb32e90 --- /dev/null +++ b/src/crypto/sha256-kdf.c @@ -0,0 +1,76 @@ +/* + * HMAC-SHA256 KDF (RFC 5295) + * Copyright (c) 2014, Jouni Malinen + * + * 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" + + +/** + * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. + */ +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) +{ + u8 T[SHA256_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA256_MAC_LEN; + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA256_MAC_LEN) + clen = SHA256_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + return -1; + } + iter++; + + if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + return -1; + } + } + + return 0; +} diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c index 0da6d130a88e..79791c06cf0b 100644 --- a/src/crypto/sha256-prf.c +++ b/src/crypto/sha256-prf.c @@ -1,6 +1,6 @@ /* * SHA256-based PRF (IEEE 802.11r) - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -28,6 +28,29 @@ */ 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; @@ -35,6 +58,7 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, 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; @@ -45,7 +69,7 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, addr[3] = length_le; len[3] = sizeof(length_le); - WPA_PUT_LE16(length_le, buf_len * 8); + WPA_PUT_LE16(length_le, buf_len_bits); pos = 0; while (pos < buf_len) { plen = buf_len - pos; @@ -57,8 +81,20 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, } 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/src/crypto/sha256.h b/src/crypto/sha256.h index fcac8004ca4f..b15f51158f32 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -1,6 +1,6 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2011, Jouni Malinen + * Copyright (c) 2003-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,8 +17,14 @@ 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/src/crypto/sha384.h b/src/crypto/sha384.h new file mode 100644 index 000000000000..e6a1fe41e1a1 --- /dev/null +++ b/src/crypto/sha384.h @@ -0,0 +1,19 @@ +/* + * SHA384 hash implementation and interface functions + * Copyright (c) 2015, Jouni Malinen + * + * 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); + +#endif /* SHA384_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index b61e43939f87..9ae95a66c9ed 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -1,6 +1,6 @@ /* * SSL/TLS interface definition - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -40,9 +40,14 @@ enum tls_fail_reason { TLS_FAIL_SUBJECT_MISMATCH = 5, TLS_FAIL_ALTSUBJECT_MISMATCH = 6, TLS_FAIL_BAD_CERTIFICATE = 7, - TLS_FAIL_SERVER_CHAIN_PROBE = 8 + 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; @@ -58,6 +63,8 @@ union tls_event_data { const struct wpabuf *cert; const u8 *hash; size_t hash_len; + const char *altsubject[TLS_MAX_ALT_SUBJECT]; + int num_altsubject; } peer_cert; struct { @@ -73,6 +80,7 @@ struct tls_config { const char *pkcs11_module_path; int fips_mode; int cert_in_cb; + const char *openssl_ciphers; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -82,6 +90,11 @@ struct tls_config { #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) /** * struct tls_connection_params - Parameters for TLS connection @@ -94,6 +107,12 @@ struct tls_config { * %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 @@ -116,7 +135,10 @@ struct tls_config { * 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(). @@ -133,6 +155,8 @@ struct tls_connection_params { 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; @@ -151,8 +175,10 @@ struct tls_connection_params { 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; }; @@ -526,4 +552,21 @@ 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); + #endif /* TLS_H */ diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index a5d72f407a8d..65db6fcc2565 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -12,61 +12,15 @@ #ifdef PKCS12_FUNCS #include #endif /* PKCS12_FUNCS */ +#if GNUTLS_VERSION_NUMBER >= 0x030103 +#include +#endif /* 3.1.3 */ #include "common.h" +#include "crypto/crypto.h" #include "tls.h" -#define WPA_TLS_RANDOM_SIZE 32 -#define WPA_TLS_MASTER_SIZE 48 - - -#if LIBGNUTLS_VERSION_NUMBER < 0x010302 -/* GnuTLS 1.3.2 added functions for using master secret. Older versions require - * use of internal structures to get the master_secret and - * {server,client}_random. - */ -#define GNUTLS_INTERNAL_STRUCTURE_HACK -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ - - -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK -/* - * It looks like gnutls does not provide access to client/server_random and - * master_key. This is somewhat unfortunate since these are needed for key - * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible - * hack that copies the gnutls_session_int definition from gnutls_int.h so that - * we can get the needed information. - */ - -typedef u8 uint8; -typedef unsigned char opaque; -typedef struct { - uint8 suite[2]; -} cipher_suite_st; - -typedef struct { - gnutls_connection_end_t entity; - gnutls_kx_algorithm_t kx_algorithm; - gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; - gnutls_mac_algorithm_t read_mac_algorithm; - gnutls_compression_method_t read_compression_algorithm; - gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; - gnutls_mac_algorithm_t write_mac_algorithm; - gnutls_compression_method_t write_compression_algorithm; - cipher_suite_st current_cipher_suite; - opaque master_secret[WPA_TLS_MASTER_SIZE]; - opaque client_random[WPA_TLS_RANDOM_SIZE]; - opaque server_random[WPA_TLS_RANDOM_SIZE]; - /* followed by stuff we are not interested in */ -} security_parameters_st; - -struct gnutls_session_int { - security_parameters_st security_parameters; - /* followed by things we are not interested in */ -}; -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ - static int tls_gnutls_ref_count = 0; struct tls_global { @@ -78,17 +32,23 @@ struct tls_global { int params_set; gnutls_certificate_credentials_t xcred; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; }; struct tls_connection { - gnutls_session session; - char *subject_match, *altsubject_match; + struct tls_global *global; + gnutls_session_t session; int read_alerts, write_alerts, failed; u8 *pre_shared_secret; size_t pre_shared_secret_len; int established; int verify_peer; + unsigned int disable_time_checks:1; struct wpabuf *push_buf; struct wpabuf *pull_buf; @@ -96,9 +56,16 @@ struct tls_connection { int params_set; gnutls_certificate_credentials_t xcred; + + char *suffix_match; + char *domain_match; + unsigned int flags; }; +static int tls_connection_verify_peer(gnutls_session_t session); + + static void tls_log_func(int level, const char *msg) { char *s, *pos; @@ -125,23 +92,15 @@ static void tls_log_func(int level, const char *msg) } -extern int wpa_debug_show_keys; - void * tls_init(const struct tls_config *conf) { struct tls_global *global; -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - /* Because of the horrible hack to get master_secret and client/server - * random, we need to make sure that the gnutls version is something - * that is expected to have same structure definition for the session - * data.. */ - const char *ver; - const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9", - "1.3.2", - NULL }; - int i; -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + if (tls_gnutls_ref_count == 0) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Library version %s (runtime) - %s (build)", + gnutls_check_version(NULL), GNUTLS_VERSION); + } global = os_zalloc(sizeof(*global)); if (global == NULL) @@ -153,28 +112,16 @@ void * tls_init(const struct tls_config *conf) } tls_gnutls_ref_count++; -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - ver = gnutls_check_version(NULL); - if (ver == NULL) { - tls_deinit(global); - return NULL; - } - wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); - for (i = 0; ok_ver[i]; i++) { - if (strcmp(ok_ver[i], ver) == 0) - break; - } - if (ok_ver[i] == NULL) { - wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " - "to be tested and enabled in tls_gnutls.c", ver); - tls_deinit(global); - return NULL; - } -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ - gnutls_global_set_log_function(tls_log_func); if (wpa_debug_show_keys) gnutls_global_set_log_level(11); + + if (conf) { + global->event_cb = conf->event_cb; + global->cb_ctx = conf->cb_ctx; + global->cert_in_cb = conf->cert_in_cb; + } + return global; } @@ -201,7 +148,7 @@ int tls_get_errors(void *ssl_ctx) } -static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, +static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; @@ -230,7 +177,7 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, } -static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, +static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; @@ -248,12 +195,7 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, static int tls_gnutls_init_session(struct tls_global *global, struct tls_connection *conn) { -#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 const char *err; -#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ - const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; - const int protos[2] = { GNUTLS_TLS1, 0 }; -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ int ret; ret = gnutls_init(&conn->session, @@ -268,7 +210,6 @@ static int tls_gnutls_init_session(struct tls_global *global, if (ret < 0) goto fail; -#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", &err); if (ret < 0) { @@ -276,19 +217,11 @@ static int tls_gnutls_init_session(struct tls_global *global, "'%s'", err); goto fail; } -#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ - ret = gnutls_certificate_type_set_priority(conn->session, cert_types); - if (ret < 0) - goto fail; - - ret = gnutls_protocol_set_priority(conn->session, protos); - if (ret < 0) - goto fail; -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); - gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn); + gnutls_session_set_ptr(conn->session, conn); return 0; @@ -309,6 +242,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->global = global; if (tls_gnutls_init_session(global, conn)) { os_free(conn); @@ -344,10 +278,10 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) gnutls_certificate_free_credentials(conn->xcred); gnutls_deinit(conn->session); os_free(conn->pre_shared_secret); - os_free(conn->subject_match); - os_free(conn->altsubject_match); wpabuf_free(conn->push_buf); wpabuf_free(conn->pull_buf); + os_free(conn->suffix_match); + os_free(conn->domain_match); os_free(conn); } @@ -405,104 +339,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) } -#if 0 -static int tls_match_altsubject(X509 *cert, const char *match) -{ - GENERAL_NAME *gen; - char *field, *tmp; - void *ext; - int i, found = 0; - size_t len; - - ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - - for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { - gen = sk_GENERAL_NAME_value(ext, i); - switch (gen->type) { - case GEN_EMAIL: - field = "EMAIL"; - break; - case GEN_DNS: - field = "DNS"; - break; - case GEN_URI: - field = "URI"; - break; - default: - field = NULL; - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " - "unsupported type=%d", gen->type); - break; - } - - if (!field) - continue; - - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", - field, gen->d.ia5->data); - len = os_strlen(field) + 1 + - strlen((char *) gen->d.ia5->data) + 1; - tmp = os_malloc(len); - if (tmp == NULL) - continue; - snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); - if (strstr(tmp, match)) - found++; - os_free(tmp); - } - - return found; -} -#endif - - -#if 0 -static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - char buf[256]; - X509 *err_cert; - int err, depth; - SSL *ssl; - struct tls_connection *conn; - char *match, *altmatch; - - err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); - err = X509_STORE_CTX_get_error(x509_ctx); - depth = X509_STORE_CTX_get_error_depth(x509_ctx); - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); - - conn = SSL_get_app_data(ssl); - match = conn ? conn->subject_match : NULL; - altmatch = conn ? conn->altsubject_match : NULL; - - if (!preverify_ok) { - wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," - " error %d (%s) depth %d for '%s'", err, - X509_verify_cert_error_string(err), depth, buf); - } else { - wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " - "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", - preverify_ok, err, - X509_verify_cert_error_string(err), depth, buf); - if (depth == 0 && match && strstr(buf, match) == NULL) { - wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " - "match with '%s'", buf, match); - preverify_ok = 0; - } else if (depth == 0 && altmatch && - !tls_match_altsubject(err_cert, altmatch)) { - wpa_printf(MSG_WARNING, "TLS: altSubjectName match " - "'%s' not found", altmatch); - preverify_ok = 0; - } - } - - return preverify_ok; -} -#endif - - int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { @@ -511,73 +347,142 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL || params == NULL) return -1; - os_free(conn->subject_match); - conn->subject_match = NULL; if (params->subject_match) { - conn->subject_match = os_strdup(params->subject_match); - if (conn->subject_match == NULL) - return -1; + wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported"); + return -1; } - os_free(conn->altsubject_match); - conn->altsubject_match = NULL; if (params->altsubject_match) { - conn->altsubject_match = os_strdup(params->altsubject_match); - if (conn->altsubject_match == NULL) + wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported"); + return -1; + } + + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (params->suffix_match) { + conn->suffix_match = os_strdup(params->suffix_match); + if (conn->suffix_match == NULL) return -1; } +#if GNUTLS_VERSION_NUMBER >= 0x030300 + os_free(conn->domain_match); + conn->domain_match = NULL; + if (params->domain_match) { + conn->domain_match = os_strdup(params->domain_match); + if (conn->domain_match == NULL) + return -1; + } +#else /* < 3.3.0 */ + if (params->domain_match) { + wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported"); + return -1; + } +#endif /* >= 3.3.0 */ + + conn->flags = params->flags; + + if (params->openssl_ciphers) { + wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + return -1; + } + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ if (params->ca_cert) { - conn->verify_peer = 1; + wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format", + params->ca_cert); ret = gnutls_certificate_set_x509_trust_file( - conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER); if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " - "in PEM format: %s", params->ca_cert, + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format", + params->ca_cert, gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_trust_file( conn->xcred, params->ca_cert, - GNUTLS_X509_FMT_DER); + GNUTLS_X509_FMT_PEM); if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read CA cert " - "'%s' in DER format: %s", + wpa_printf(MSG_DEBUG, + "Failed to read CA cert '%s' in PEM format: %s", params->ca_cert, gnutls_strerror(ret)); return -1; } } + } else if (params->ca_cert_blob) { + gnutls_datum_t ca; + + ca.data = (unsigned char *) params->ca_cert_blob; + ca.size = params->ca_cert_blob_len; + + ret = gnutls_certificate_set_x509_trust_mem( + conn->xcred, &ca, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "Failed to parse CA cert in DER format: %s", + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_mem( + conn->xcred, &ca, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "Failed to parse CA cert in PEM format: %s", + gnutls_strerror(ret)); + return -1; + } + } + } else if (params->ca_path) { + wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported"); + return -1; + } + + conn->disable_time_checks = 0; + if (params->ca_cert || params->ca_cert_blob) { + conn->verify_peer = 1; + gnutls_certificate_set_verify_function( + conn->xcred, tls_connection_verify_peer); if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } -#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { + conn->disable_time_checks = 1; gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { - /* TODO: private_key_passwd? */ +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_file2( + conn->xcred, params->client_cert, params->private_key, + GNUTLS_X509_FMT_DER, params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, params->private_key, - GNUTLS_X509_FMT_PEM); + GNUTLS_X509_FMT_DER); +#endif if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in PEM format: %s", gnutls_strerror(ret)); + "in DER format: %s", gnutls_strerror(ret)); +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_file2( + conn->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_PEM, + params->private_key_passwd, 0); +#else ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_DER); + params->private_key, GNUTLS_X509_FMT_PEM); +#endif if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " - "cert/key in DER format: %s", + "cert/key in PEM format: %s", gnutls_strerror(ret)); return ret; } @@ -586,7 +491,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 ret = gnutls_certificate_set_x509_simple_pkcs12_file( conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -596,7 +500,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } else pkcs12_ok = 1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ #endif /* PKCS12_FUNCS */ if (!pkcs12_ok) { @@ -604,8 +507,82 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, "included"); return -1; } + } else if (params->client_cert_blob && params->private_key_blob) { + gnutls_datum_t cert, key; + + cert.data = (unsigned char *) params->client_cert_blob; + cert.size = params->client_cert_blob_len; + key.data = (unsigned char *) params->private_key_blob; + key.size = params->private_key_blob_len; + +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_mem2( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER, + params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ + ret = gnutls_certificate_set_x509_key_mem( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER); +#endif + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in DER format: %s", gnutls_strerror(ret)); +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_mem2( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM, + params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ + ret = gnutls_certificate_set_x509_key_mem( + conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM); +#endif + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in PEM format: %s", + gnutls_strerror(ret)); + return ret; + } + } + } else if (params->private_key_blob) { +#ifdef PKCS12_FUNCS + gnutls_datum_t key; + + key.data = (unsigned char *) params->private_key_blob; + key.size = params->private_key_blob_len; + + /* Try to load in PKCS#12 format */ + ret = gnutls_certificate_set_x509_simple_pkcs12_mem( + conn->xcred, &key, GNUTLS_X509_FMT_DER, + params->private_key_passwd); + if (ret != 0) { + wpa_printf(MSG_DEBUG, "Failed to load private_key in " + "PKCS#12 format: %s", gnutls_strerror(ret)); + return -1; + } +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included"); + return -1; +#endif /* PKCS12_FUNCS */ } +#if GNUTLS_VERSION_NUMBER >= 0x030103 + if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) { + ret = gnutls_ocsp_status_request_enable_client(conn->session, + NULL, 0, NULL); + if (ret != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: Failed to enable OCSP client"); + return -1; + } + } +#else /* 3.1.3 */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "GnuTLS: OCSP not supported by this version of GnuTLS"); + return -1; + } +#endif /* 3.1.3 */ + conn->params_set = 1; ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, @@ -643,17 +620,17 @@ int tls_global_set_params(void *tls_ctx, if (params->ca_cert) { ret = gnutls_certificate_set_x509_trust_file( - global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " - "in PEM format: %s", params->ca_cert, + "in DER format: %s", params->ca_cert, gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_trust_file( global->xcred, params->ca_cert, - GNUTLS_X509_FMT_DER); + GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read CA cert " - "'%s' in DER format: %s", + "'%s' in PEM format: %s", params->ca_cert, gnutls_strerror(ret)); goto fail; @@ -666,29 +643,27 @@ int tls_global_set_params(void *tls_ctx, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } -#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( global->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { /* TODO: private_key_passwd? */ ret = gnutls_certificate_set_x509_key_file( global->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_PEM); + params->private_key, GNUTLS_X509_FMT_DER); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in PEM format: %s", gnutls_strerror(ret)); + "in DER format: %s", gnutls_strerror(ret)); ret = gnutls_certificate_set_x509_key_file( global->xcred, params->client_cert, - params->private_key, GNUTLS_X509_FMT_DER); + params->private_key, GNUTLS_X509_FMT_PEM); if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " - "cert/key in DER format: %s", + "cert/key in PEM format: %s", gnutls_strerror(ret)); goto fail; } @@ -697,7 +672,6 @@ int tls_global_set_params(void *tls_ctx, int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 ret = gnutls_certificate_set_x509_simple_pkcs12_file( global->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -707,7 +681,6 @@ int tls_global_set_params(void *tls_ctx, goto fail; } else pkcs12_ok = 1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ #endif /* PKCS12_FUNCS */ if (!pkcs12_ok) { @@ -752,37 +725,23 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - security_parameters_st *sec; -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ +#if GNUTLS_VERSION_NUMBER >= 0x030012 + gnutls_datum_t client, server; if (conn == NULL || conn->session == NULL || keys == NULL) return -1; os_memset(keys, 0, sizeof(*keys)); - -#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 -#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK - sec = &conn->session->security_parameters; - keys->master_key = sec->master_secret; - keys->master_key_len = WPA_TLS_MASTER_SIZE; - keys->client_random = sec->client_random; - keys->server_random = sec->server_random; -#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ - keys->client_random = - (u8 *) gnutls_session_get_client_random(conn->session); - keys->server_random = - (u8 *) gnutls_session_get_server_random(conn->session); - /* No access to master_secret */ -#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ - -#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 - keys->client_random_len = WPA_TLS_RANDOM_SIZE; - keys->server_random_len = WPA_TLS_RANDOM_SIZE; -#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ + gnutls_session_get_random(conn->session, &client, &server); + keys->client_random = client.data; + keys->server_random = server.data; + keys->client_random_len = client.size; + keys->server_random_len = client.size; return 0; +#else /* 3.0.18 */ + return -1; +#endif /* 3.0.18 */ } @@ -790,86 +749,317 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, const char *label, int server_random_first, u8 *out, size_t out_len) { -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 if (conn == NULL || conn->session == NULL) return -1; return gnutls_prf(conn->session, os_strlen(label), label, server_random_first, 0, NULL, out_len, (char *) out); -#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +} + + +static void gnutls_tls_fail_event(struct tls_connection *conn, + const gnutls_datum_t *cert, int depth, + const char *subject, const char *err_str, + enum tls_fail_reason reason) +{ + union tls_event_data ev; + struct tls_global *global = conn->global; + struct wpabuf *cert_buf = NULL; + + if (global->event_cb == NULL) + return; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.depth = depth; + ev.cert_fail.subject = subject ? subject : ""; + ev.cert_fail.reason = reason; + ev.cert_fail.reason_txt = err_str; + if (cert) { + cert_buf = wpabuf_alloc_copy(cert->data, cert->size); + ev.cert_fail.cert = cert_buf; + } + global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert_buf); +} + + +#if GNUTLS_VERSION_NUMBER < 0x030300 +static int server_eku_purpose(gnutls_x509_crt_t cert) +{ + unsigned int i; + + for (i = 0; ; i++) { + char oid[128]; + size_t oid_size = sizeof(oid); + int res; + + res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid, + &oid_size, NULL); + if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (i == 0) { + /* No EKU - assume any use allowed */ + return 1; + } + break; + } + + if (res < 0) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU"); + return 0; + } + + wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid); + if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 || + os_strcmp(oid, GNUTLS_KP_ANY) == 0) + return 1; + } + + return 0; +} +#endif /* < 3.3.0 */ + + +static int check_ocsp(struct tls_connection *conn, gnutls_session_t session, + gnutls_alert_description_t *err) +{ +#if GNUTLS_VERSION_NUMBER >= 0x030103 + gnutls_datum_t response, buf; + gnutls_ocsp_resp_t resp; + unsigned int cert_status; + int res; + + if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP))) + return 0; + + if (!gnutls_ocsp_status_request_is_checked(session, 0)) { + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "GnuTLS: No valid OCSP response received"); + goto ocsp_error; + } + + wpa_printf(MSG_DEBUG, + "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required"); + return 0; + } + + /* + * GnuTLS has already verified the OCSP response in + * check_ocsp_response() and rejected handshake if the certificate was + * found to be revoked. However, if the response indicates that the + * status is unknown, handshake continues and reaches here. We need to + * re-import the OCSP response to check for unknown certificate status, + * but we do not need to repeat gnutls_ocsp_resp_check_crt() and + * gnutls_ocsp_resp_verify_direct() calls. + */ + + res = gnutls_ocsp_status_request_get(session, &response); + if (res != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: OCSP response was received, but it was not valid"); + goto ocsp_error; + } + + if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS) + goto ocsp_error; + + res = gnutls_ocsp_resp_import(resp, &response); + if (res != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: Could not parse received OCSP response: %s", + gnutls_strerror(res)); + gnutls_ocsp_resp_deinit(resp); + goto ocsp_error; + } + + res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf); + if (res == GNUTLS_E_SUCCESS) { + wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data); + gnutls_free(buf.data); + } + + res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, + NULL, &cert_status, NULL, + NULL, NULL, NULL); + gnutls_ocsp_resp_deinit(resp); + if (res != GNUTLS_E_SUCCESS) { + wpa_printf(MSG_INFO, + "GnuTLS: Failed to extract OCSP information: %s", + gnutls_strerror(res)); + goto ocsp_error; + } + + if (cert_status == GNUTLS_OCSP_CERT_GOOD) { + wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good"); + } else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP cert status: revoked"); + goto ocsp_error; + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP cert status: unknown"); + if (conn->flags & TLS_CONN_REQUIRE_OCSP) + goto ocsp_error; + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP was not required, so allow connection to continue"); + } + + return 0; + +ocsp_error: + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "bad certificate status response", + TLS_FAIL_REVOKED); + *err = GNUTLS_A_CERTIFICATE_REVOKED; return -1; -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#else /* GnuTLS 3.1.3 or newer */ + return 0; +#endif /* GnuTLS 3.1.3 or newer */ } -static int tls_connection_verify_peer(struct tls_connection *conn, - gnutls_alert_description_t *err) +static int tls_connection_verify_peer(gnutls_session_t session) { + struct tls_connection *conn; unsigned int status, num_certs, i; struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; + gnutls_alert_description_t err; + int res; + + conn = gnutls_session_get_ptr(session); + if (!conn->verify_peer) { + wpa_printf(MSG_DEBUG, + "GnuTLS: No peer certificate verification enabled"); + return 0; + } + + wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate"); + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + { + gnutls_typed_vdata_st data[1]; + unsigned int elements = 0; - if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { + os_memset(data, 0, sizeof(data)); + if (!conn->global->server) { + data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID; + data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER; + elements++; + } + res = gnutls_certificate_verify_peers(session, data, 1, + &status); + } +#else /* < 3.3.0 */ + res = gnutls_certificate_verify_peers2(session, &status); +#endif + if (res < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); - *err = GNUTLS_A_INTERNAL_ERROR; - return -1; + err = GNUTLS_A_INTERNAL_ERROR; + goto out; + } + +#if GNUTLS_VERSION_NUMBER >= 0x030104 + { + gnutls_datum_t info; + int ret, type; + + type = gnutls_certificate_type_get(session); + ret = gnutls_certificate_verification_status_print(status, type, + &info, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to print verification status"); + err = GNUTLS_A_INTERNAL_ERROR; + goto out; + } + wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data); + gnutls_free(info.data); + } +#endif /* GnuTLS 3.1.4 or newer */ + + certs = gnutls_certificate_get_peers(session, &num_certs); + if (certs == NULL || num_certs == 0) { + wpa_printf(MSG_INFO, "TLS: No peer certificate chain received"); + err = GNUTLS_A_UNKNOWN_CA; + goto out; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); - *err = GNUTLS_A_INTERNAL_ERROR; if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); - *err = GNUTLS_A_INSUFFICIENT_SECURITY; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate uses insecure algorithm", + TLS_FAIL_BAD_CERTIFICATE); + err = GNUTLS_A_INSUFFICIENT_SECURITY; + goto out; } -#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); - *err = GNUTLS_A_CERTIFICATE_EXPIRED; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate not yet valid", + TLS_FAIL_NOT_YET_VALID); + err = GNUTLS_A_CERTIFICATE_EXPIRED; + goto out; } if (status & GNUTLS_CERT_EXPIRED) { wpa_printf(MSG_INFO, "TLS: Certificate expired"); - *err = GNUTLS_A_CERTIFICATE_EXPIRED; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate has expired", + TLS_FAIL_EXPIRED); + err = GNUTLS_A_CERTIFICATE_EXPIRED; + goto out; } -#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ - return -1; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "untrusted certificate", + TLS_FAIL_UNTRUSTED); + err = GNUTLS_A_INTERNAL_ERROR; + goto out; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); - *err = GNUTLS_A_UNKNOWN_CA; - return -1; + gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found", + TLS_FAIL_UNTRUSTED); + err = GNUTLS_A_UNKNOWN_CA; + goto out; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); - *err = GNUTLS_A_CERTIFICATE_REVOKED; - return -1; + gnutls_tls_fail_event(conn, NULL, 0, NULL, + "certificate revoked", + TLS_FAIL_REVOKED); + err = GNUTLS_A_CERTIFICATE_REVOKED; + goto out; } - os_get_time(&now); - - certs = gnutls_certificate_get_peers(conn->session, &num_certs); - if (certs == NULL) { - wpa_printf(MSG_INFO, "TLS: No peer certificate chain " - "received"); - *err = GNUTLS_A_UNKNOWN_CA; - return -1; + if (status != 0) { + wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d", + status); + err = GNUTLS_A_INTERNAL_ERROR; + goto out; } + if (check_ocsp(conn, session, &err)) + goto out; + + os_get_time(&now); + for (i = 0; i < num_certs; i++) { char *buf; size_t len; if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); - *err = GNUTLS_A_BAD_CERTIFICATE; - return -1; + err = GNUTLS_A_BAD_CERTIFICATE; + goto out; } if (gnutls_x509_crt_import(cert, &certs[i], @@ -877,8 +1067,8 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); - *err = GNUTLS_A_BAD_CERTIFICATE; - return -1; + err = GNUTLS_A_BAD_CERTIFICATE; + goto out; } gnutls_x509_crt_get_dn(cert, NULL, &len); @@ -891,26 +1081,128 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", i + 1, num_certs, buf); - if (i == 0) { - /* TODO: validate subject_match and altsubject_match */ + if (conn->global->event_cb) { + struct wpabuf *cert_buf = NULL; + union tls_event_data ev; +#ifdef CONFIG_SHA256 + u8 hash[32]; + const u8 *_addr[1]; + size_t _len[1]; +#endif /* CONFIG_SHA256 */ + + os_memset(&ev, 0, sizeof(ev)); + if (conn->global->cert_in_cb) { + cert_buf = wpabuf_alloc_copy(certs[i].data, + certs[i].size); + ev.peer_cert.cert = cert_buf; + } +#ifdef CONFIG_SHA256 + _addr[0] = certs[i].data; + _len[0] = certs[i].size; + if (sha256_vector(1, _addr, _len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } +#endif /* CONFIG_SHA256 */ + ev.peer_cert.depth = i; + ev.peer_cert.subject = buf; + conn->global->event_cb(conn->global->cb_ctx, + TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert_buf); } - os_free(buf); + if (i == 0) { + if (conn->suffix_match && + !gnutls_x509_crt_check_hostname( + cert, conn->suffix_match)) { + wpa_printf(MSG_WARNING, + "TLS: Domain suffix match '%s' not found", + conn->suffix_match); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); + err = GNUTLS_A_BAD_CERTIFICATE; + gnutls_x509_crt_deinit(cert); + os_free(buf); + goto out; + } - if (gnutls_x509_crt_get_expiration_time(cert) < now.sec || - gnutls_x509_crt_get_activation_time(cert) > now.sec) { +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (conn->domain_match && + !gnutls_x509_crt_check_hostname2( + cert, conn->domain_match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + wpa_printf(MSG_WARNING, + "TLS: Domain match '%s' not found", + conn->domain_match); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); + err = GNUTLS_A_BAD_CERTIFICATE; + gnutls_x509_crt_deinit(cert); + os_free(buf); + goto out; + } +#endif /* >= 3.3.0 */ + + /* TODO: validate altsubject_match. + * For now, any such configuration is rejected in + * tls_connection_set_params() */ + +#if GNUTLS_VERSION_NUMBER < 0x030300 + /* + * gnutls_certificate_verify_peers() not available, so + * need to check EKU separately. + */ + if (!conn->global->server && + !server_eku_purpose(cert)) { + wpa_printf(MSG_WARNING, + "GnuTLS: No server EKU"); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "No server EKU", + TLS_FAIL_BAD_CERTIFICATE); + err = GNUTLS_A_BAD_CERTIFICATE; + gnutls_x509_crt_deinit(cert); + os_free(buf); + goto out; + } +#endif /* < 3.3.0 */ + } + + if (!conn->disable_time_checks && + (gnutls_x509_crt_get_expiration_time(cert) < now.sec || + gnutls_x509_crt_get_activation_time(cert) > now.sec)) { wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " "not valid at this time", i + 1, num_certs); + gnutls_tls_fail_event( + conn, &certs[i], i, buf, + "Certificate is not valid at this time", + TLS_FAIL_EXPIRED); gnutls_x509_crt_deinit(cert); - *err = GNUTLS_A_CERTIFICATE_EXPIRED; - return -1; + os_free(buf); + err = GNUTLS_A_CERTIFICATE_EXPIRED; + goto out; } + os_free(buf); + gnutls_x509_crt_deinit(cert); } + if (conn->global->event_cb != NULL) + conn->global->event_cb(conn->global->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + return 0; + +out: + conn->failed++; + gnutls_alert_send(session, GNUTLS_AL_FATAL, err); + return GNUTLS_E_CERTIFICATE_ERROR; } @@ -968,6 +1260,8 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, ret = gnutls_handshake(conn->session); if (ret < 0) { + gnutls_alert_description_t alert; + switch (ret) { case GNUTLS_E_AGAIN: if (global->server && conn->established && @@ -978,10 +1272,20 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: + alert = gnutls_alert_get(conn->session); wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", - __func__, gnutls_alert_get_name( - gnutls_alert_get(conn->session))); + __func__, gnutls_alert_get_name(alert)); conn->read_alerts++; + if (conn->global->event_cb != NULL) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 0; + ev.alert.type = gnutls_alert_get_name(alert); + ev.alert.description = ev.alert.type; + conn->global->event_cb(conn->global->cb_ctx, + TLS_ALERT, &ev); + } /* continue */ default: wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " @@ -990,18 +1294,21 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, } } else { size_t size; - gnutls_alert_description_t err; - if (conn->verify_peer && - tls_connection_verify_peer(conn, &err)) { - wpa_printf(MSG_INFO, "TLS: Peer certificate chain " - "failed validation"); - conn->failed++; - gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); - goto out; + wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); + +#if GNUTLS_VERSION_NUMBER >= 0x03010a + { + char *desc; + + desc = gnutls_session_get_desc(conn->session); + if (desc) { + wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc); + gnutls_free(desc); + } } +#endif /* GnuTLS 3.1.10 or newer */ - wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ @@ -1025,7 +1332,6 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, *appl_data = gnutls_get_appl_data(conn); } -out: out_data = conn->push_buf; conn->push_buf = NULL; return out_data; @@ -1190,3 +1496,10 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, { return -1; } + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s", + GNUTLS_VERSION, gnutls_check_version(NULL)); +} diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 91f06900328a..0c955da29f1d 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -28,6 +28,7 @@ struct tls_global { struct tls_connection { struct tlsv1_client *client; struct tlsv1_server *server; + struct tls_global *global; }; @@ -85,6 +86,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx) conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->global = global; #ifdef CONFIG_TLS_INTERNAL_CLIENT if (!global->server) { @@ -109,6 +111,28 @@ struct tls_connection * tls_connection_init(void *tls_ctx) } +#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) @@ -166,6 +190,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (cred == NULL) return -1; + if (params->subject_match) { + wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + return -1; + } + + if (params->altsubject_match) { + wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + return -1; + } + + if (params->suffix_match) { + wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + return -1; + } + + if (params->domain_match) { + wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + return -1; + } + + if (params->openssl_ciphers) { + wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + return -1; + } + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { @@ -628,3 +677,9 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, #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"); +} diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 1a1092a184b5..a6d210afcf0f 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -192,3 +192,9 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "none"); +} diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c deleted file mode 100644 index c53c192a1cde..000000000000 --- a/src/crypto/tls_nss.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * SSL/TLS interface functions for NSS - * Copyright (c) 2009, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "tls.h" - -static int tls_nss_ref_count = 0; - -static PRDescIdentity nss_layer_id; - - -struct tls_connection { - PRFileDesc *fd; - - int established; - int verify_peer; - u8 *push_buf, *pull_buf, *pull_buf_offset; - size_t push_buf_len, pull_buf_len; -}; - - -static PRStatus nss_io_close(PRFileDesc *fd) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O close"); - return PR_SUCCESS; -} - - -static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount); - return PR_FAILURE; -} - - -static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount); - return PR_FAILURE; -} - - -static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov, - PRInt32 iov_size, PRIntervalTime timeout) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size); - return PR_FAILURE; -} - - -static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - struct tls_connection *conn = (struct tls_connection *) fd->secret; - u8 *end; - - wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount); - - if (conn->pull_buf == NULL) { - wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet"); - return PR_FAILURE; - } - - end = conn->pull_buf + conn->pull_buf_len; - if (end - conn->pull_buf_offset < amount) - amount = end - conn->pull_buf_offset; - os_memcpy(buf, conn->pull_buf_offset, amount); - conn->pull_buf_offset += amount; - if (conn->pull_buf_offset == end) { - wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); - os_free(conn->pull_buf); - conn->pull_buf = conn->pull_buf_offset = NULL; - conn->pull_buf_len = 0; - } else { - wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", - __func__, - (unsigned long) (end - conn->pull_buf_offset)); - } - return amount; -} - - -static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - struct tls_connection *conn = (struct tls_connection *) fd->secret; - u8 *nbuf; - - wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); - wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount); - - nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount); - if (nbuf == NULL) { - wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the " - "data to be sent"); - return PR_FAILURE; - } - os_memcpy(nbuf + conn->push_buf_len, buf, amount); - conn->push_buf = nbuf; - conn->push_buf_len += amount; - - return amount; -} - - -static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, - PRIntn flags, PRNetAddr *addr, - PRIntervalTime timeout) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); - return PR_FAILURE; -} - - -static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount, - PRIntn flags, const PRNetAddr *addr, - PRIntervalTime timeout) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); - return PR_FAILURE; -} - - -static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr) -{ - wpa_printf(MSG_DEBUG, "NSS: I/O getpeername"); - - /* - * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a - * fake IPv4 address to work around this even though we are not really - * using TCP. - */ - os_memset(addr, 0, sizeof(*addr)); - addr->inet.family = PR_AF_INET; - - return PR_SUCCESS; -} - - -static PRStatus nss_io_getsocketoption(PRFileDesc *fd, - PRSocketOptionData *data) -{ - switch (data->option) { - case PR_SockOpt_Nonblocking: - wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)"); - data->value.non_blocking = PR_TRUE; - return PR_SUCCESS; - default: - wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)", - data->option); - return PR_FAILURE; - } -} - - -static const PRIOMethods nss_io = { - PR_DESC_LAYERED, - nss_io_close, - nss_io_read, - nss_io_write, - NULL /* available */, - NULL /* available64 */, - NULL /* fsync */, - NULL /* fseek */, - NULL /* fseek64 */, - NULL /* fileinfo */, - NULL /* fileinfo64 */, - nss_io_writev, - NULL /* connect */, - NULL /* accept */, - NULL /* bind */, - NULL /* listen */, - NULL /* shutdown */, - nss_io_recv, - nss_io_send, - nss_io_recvfrom, - nss_io_sendto, - NULL /* poll */, - NULL /* acceptread */, - NULL /* transmitfile */, - NULL /* getsockname */, - nss_io_getpeername, - NULL /* reserved_fn_6 */, - NULL /* reserved_fn_5 */, - nss_io_getsocketoption, - NULL /* setsocketoption */, - NULL /* sendfile */, - NULL /* connectcontinue */, - NULL /* reserved_fn_3 */, - NULL /* reserved_fn_2 */, - NULL /* reserved_fn_1 */, - NULL /* reserved_fn_0 */ -}; - - -static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg) -{ - wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__); - return NULL; -} - - -void * tls_init(const struct tls_config *conf) -{ - char *dir; - - tls_nss_ref_count++; - if (tls_nss_ref_count > 1) - return (void *) 1; - - PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); - - nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant"); - - PK11_SetPasswordFunc(nss_password_cb); - - dir = getenv("SSL_DIR"); - if (dir) { - if (NSS_Init(dir) != SECSuccess) { - wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) " - "failed", dir); - return NULL; - } - } else { - if (NSS_NoDB_Init(NULL) != SECSuccess) { - wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) " - "failed"); - return NULL; - } - } - - if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) != - SECSuccess || - SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess || - SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess || - SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) { - wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed"); - return NULL; - } - - if (NSS_SetDomesticPolicy() != SECSuccess) { - wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed"); - return NULL; - } - - return (void *) 1; -} - -void tls_deinit(void *ssl_ctx) -{ - tls_nss_ref_count--; - if (tls_nss_ref_count == 0) { - if (NSS_Shutdown() != SECSuccess) - wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed"); - } -} - - -int tls_get_errors(void *tls_ctx) -{ - return 0; -} - - -static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd) -{ - struct tls_connection *conn = arg; - SECStatus res = SECSuccess; - PRErrorCode err; - CERTCertificate *cert; - char *subject, *issuer; - - err = PR_GetError(); - if (IS_SEC_ERROR(err)) - wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err " - "%d)", err - SEC_ERROR_BASE); - else - wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)", - err); - cert = SSL_PeerCertificate(fd); - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'", - subject, issuer); - CERT_DestroyCertificate(cert); - PR_Free(subject); - PR_Free(issuer); - if (conn->verify_peer) - res = SECFailure; - - return res; -} - - -static void nss_handshake_cb(PRFileDesc *fd, void *client_data) -{ - struct tls_connection *conn = client_data; - wpa_printf(MSG_DEBUG, "NSS: Handshake completed"); - conn->established = 1; -} - - -struct tls_connection * tls_connection_init(void *tls_ctx) -{ - struct tls_connection *conn; - - conn = os_zalloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - - conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io); - if (conn->fd == NULL) { - os_free(conn); - return NULL; - } - conn->fd->secret = (void *) conn; - - conn->fd = SSL_ImportFD(NULL, conn->fd); - if (conn->fd == NULL) { - os_free(conn); - return NULL; - } - - if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess || - SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != - SECSuccess || - SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != - SECSuccess || - SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess || - SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess || - SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) != - SECSuccess) { - wpa_printf(MSG_ERROR, "NSS: Failed to set options"); - PR_Close(conn->fd); - os_free(conn); - return NULL; - } - - SSL_ResetHandshake(conn->fd, PR_FALSE); - - return conn; -} - - -void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) -{ - PR_Close(conn->fd); - os_free(conn->push_buf); - os_free(conn->pull_buf); - os_free(conn); -} - - -int tls_connection_established(void *tls_ctx, struct tls_connection *conn) -{ - return conn->established; -} - - -int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params) -{ - wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__); - return 0; -} - - -int tls_global_set_params(void *tls_ctx, - const struct tls_connection_params *params) -{ - return -1; -} - - -int tls_global_set_verify(void *tls_ctx, int check_crl) -{ - return -1; -} - - -int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer) -{ - conn->verify_peer = verify_peer; - return 0; -} - - -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys) -{ - /* NSS does not export master secret or client/server random. */ - return -1; -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len) -{ - if (conn == NULL || server_random_first) { - wpa_printf(MSG_INFO, "NSS: Unsupported PRF request " - "(server_random_first=%d)", - server_random_first); - return -1; - } - - if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) != - SECSuccess) { - wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor " - "(label='%s' out_len=%d", label, (int) out_len); - return -1; - } - - return 0; -} - - -struct wpabuf * tls_connection_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - struct wpabuf *out_data; - - wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u", - in_data ? (unsigned int) wpabuf_len(in_data) : 0); - - if (appl_data) - *appl_data = NULL; - - if (in_data && wpabuf_len(in_data) > 0) { - if (conn->pull_buf) { - wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " - "pull_buf", __func__, - (unsigned long) conn->pull_buf_len); - os_free(conn->pull_buf); - } - conn->pull_buf = os_malloc(wpabuf_len(in_data)); - if (conn->pull_buf == NULL) - return NULL; - os_memcpy(conn->pull_buf, wpabuf_head(in_data), - wpabuf_len(in_data)); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = wpabuf_len(in_data); - } - - SSL_ForceHandshake(conn->fd); - - if (conn->established && conn->push_buf == NULL) { - /* Need to return something to get final TLS ACK. */ - conn->push_buf = os_malloc(1); - } - - if (conn->push_buf == NULL) - return NULL; - out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len); - if (out_data == NULL) - os_free(conn->push_buf); - conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_data; -} - - -struct wpabuf * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data, - struct wpabuf **appl_data) -{ - return NULL; -} - - -struct wpabuf * tls_connection_encrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - PRInt32 res; - struct wpabuf *buf; - - wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes", - (int) wpabuf_len(in_data)); - res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0, - 0); - if (res < 0) { - wpa_printf(MSG_ERROR, "NSS: Encryption failed"); - return NULL; - } - if (conn->push_buf == NULL) - return NULL; - buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len); - if (buf == NULL) - os_free(conn->push_buf); - conn->push_buf = NULL; - conn->push_buf_len = 0; - return buf; -} - - -struct wpabuf * tls_connection_decrypt(void *tls_ctx, - struct tls_connection *conn, - const struct wpabuf *in_data) -{ - PRInt32 res; - struct wpabuf *out; - - wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes", - (int) wpabuf_len(in_data)); - if (conn->pull_buf) { - wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " - "pull_buf", __func__, - (unsigned long) conn->pull_buf_len); - os_free(conn->pull_buf); - } - conn->pull_buf = os_malloc(wpabuf_len(in_data)); - if (conn->pull_buf == NULL) - return NULL; - os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data)); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = wpabuf_len(in_data); - - /* - * Even though we try to disable TLS compression, it is possible that - * this cannot be done with all TLS libraries. Add extra buffer space - * to handle the possibility of the decrypted data being longer than - * input data. - */ - out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); - if (out == NULL) - return NULL; - - res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0); - wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res); - if (res < 0) { - wpabuf_free(out); - return NULL; - } - wpabuf_put(out, res); - - return out; -} - - -int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, - u8 *ciphers) -{ - return -1; -} - - -int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, - char *buf, size_t buflen) -{ - 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) -{ - return -1; -} - - -int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_get_write_alerts(void *tls_ctx, - struct tls_connection *conn) -{ - return 0; -} - - -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} - - -int tls_connection_set_session_ticket_cb(void *tls_ctx, - struct tls_connection *conn, - tls_session_ticket_cb cb, - void *ctx) -{ - return -1; -} diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 2c3db473258b..52db8fc076ac 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,9 +10,11 @@ #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE +#ifndef ANDROID #define OPENSSL_NO_ENGINE #endif #endif +#endif #include #include @@ -22,51 +24,74 @@ #include #endif /* OPENSSL_NO_ENGINE */ -#ifdef ANDROID -#include -#include "keystore_get.h" -#endif /* ANDROID */ - #include "common.h" #include "crypto.h" #include "tls.h" -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL -#define OPENSSL_d2i_TYPE const unsigned char ** -#else -#define OPENSSL_d2i_TYPE unsigned char ** +#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) +#define OPENSSL_SUPPORTS_CTX_APP_DATA #endif -#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT -#ifdef SSL_OP_NO_TICKET -/* - * Session ticket override patch was merged into OpenSSL 0.9.9 tree on - * 2008-11-15. This version uses a bit different API compared to the old patch. - */ -#define CONFIG_OPENSSL_TICKET_OVERRIDE +#if OPENSSL_VERSION_NUMBER < 0x10000000L +/* ERR_remove_thread_state replaces ERR_remove_state and the latter is + * deprecated. However, OpenSSL 0.9.8 doesn't include + * ERR_remove_thread_state. */ +#define ERR_remove_thread_state(tid) ERR_remove_state(0) #endif + +#if defined(OPENSSL_IS_BORINGSSL) +/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */ +typedef size_t stack_index_t; +#else +typedef int stack_index_t; #endif +#ifdef SSL_set_tlsext_status_type +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_OCSP +#include +#endif /* OPENSSL_NO_TLSEXT */ +#endif /* SSL_set_tlsext_status_type */ + +#ifdef ANDROID +#include +#include + +static BIO * BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + uint8_t *value = NULL; + int length = keystore_get(key, strlen(key), &value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) + BIO_write(bio, value, length); + free(value); + return bio; +} +#endif /* ANDROID */ + static int tls_openssl_ref_count = 0; -struct tls_global { +struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; int cert_in_cb; + char *ocsp_stapling_response; }; -static struct tls_global *tls_global = NULL; +static struct tls_context *tls_global = NULL; struct tls_connection { + struct tls_context *context; + SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; #ifndef OPENSSL_NO_ENGINE ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ - char *subject_match, *altsubject_match; + char *subject_match, *altsubject_match, *suffix_match, *domain_match; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -79,13 +104,32 @@ struct tls_connection { unsigned int ca_cert_verify:1; unsigned int cert_probe:1; unsigned int server_cert_only:1; + unsigned int invalid_hb_used:1; u8 srv_cert_hash[32]; unsigned int flags; + + X509 *peer_cert; + X509 *peer_issuer; + X509 *peer_issuer_issuer; }; +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + if (context == NULL) + return NULL; + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + return context; +} + + #ifdef CONFIG_NO_STDOUT_DEBUG static void _tls_show_errors(void) @@ -351,7 +395,8 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name) goto err; } - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &priv->cert->pbCertEncoded, priv->cert->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " @@ -451,7 +496,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) } while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &ctx->pbCertEncoded, ctx->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process " @@ -511,6 +557,7 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) wpa_printf(MSG_DEBUG, "SSL: %s:%s", str, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { + struct tls_connection *conn = SSL_get_app_data((SSL *) ssl); wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? "read (remote end reported an error)" : @@ -518,21 +565,19 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); if ((ret >> 8) == SSL3_AL_FATAL) { - struct tls_connection *conn = - SSL_get_app_data((SSL *) ssl); if (where & SSL_CB_READ) conn->read_alerts++; else conn->write_alerts++; } - if (tls_global->event_cb != NULL) { + if (conn->context->event_cb != NULL) { union tls_event_data ev; + struct tls_context *context = conn->context; os_memset(&ev, 0, sizeof(ev)); ev.alert.is_local = !(where & SSL_CB_READ); ev.alert.type = SSL_alert_type_string_long(ret); ev.alert.description = SSL_alert_desc_string_long(ret); - tls_global->event_cb(tls_global->cb_ctx, TLS_ALERT, - &ev); + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", @@ -644,12 +689,15 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, NULL, NULL }; - if (!pkcs11_so_path || !pkcs11_module_path) + if (!pkcs11_so_path) return 0; pre_cmd[1] = pkcs11_so_path; pre_cmd[3] = engine_id; - post_cmd[1] = pkcs11_module_path; + if (pkcs11_module_path) + post_cmd[1] = pkcs11_module_path; + else + post_cmd[0] = NULL; wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", pkcs11_so_path); @@ -690,17 +738,13 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; + struct tls_context *context; + const char *ciphers; if (tls_openssl_ref_count == 0) { - tls_global = os_zalloc(sizeof(*tls_global)); - if (tls_global == NULL) + tls_global = context = tls_context_new(conf); + if (context == NULL) return NULL; - if (conf) { - tls_global->event_cb = conf->event_cb; - tls_global->cb_ctx = conf->cb_ctx; - tls_global->cert_in_cb = conf->cert_in_cb; - } - #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { @@ -727,7 +771,7 @@ void * tls_init(const struct tls_config *conf) #endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +#ifndef OPENSSL_NO_SHA256 EVP_add_digest(EVP_sha256()); #endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded @@ -746,23 +790,48 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } else { +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + /* Newer OpenSSL can store app-data per-SSL */ + context = tls_context_new(conf); + if (context == NULL) + return NULL; +#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + context = tls_global; +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; - ssl = SSL_CTX_new(TLSv1_method()); - if (ssl == NULL) + ssl = SSL_CTX_new(SSLv23_method()); + if (ssl == NULL) { + tls_openssl_ref_count--; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (tls_openssl_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } return NULL; + } + + SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + SSL_CTX_set_app_data(ssl, context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ #ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); + ERR_load_ENGINE_strings(); + ENGINE_load_dynamic(); + if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || conf->pkcs11_module_path)) { - wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); - ERR_load_ENGINE_strings(); - ENGINE_load_dynamic(); - if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { @@ -772,6 +841,18 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_NO_ENGINE */ + if (conf && conf->openssl_ciphers) + ciphers = conf->openssl_ciphers; + else + ciphers = "DEFAULT:!EXP:!LOW"; + if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to set cipher string '%s'", + ciphers); + tls_deinit(ssl); + return NULL; + } + return ssl; } @@ -779,6 +860,11 @@ void * tls_init(const struct tls_config *conf) void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_context *context = SSL_CTX_get_app_data(ssl); + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -787,9 +873,11 @@ void tls_deinit(void *ssl_ctx) ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); + ERR_remove_thread_state(NULL); ERR_free_strings(); EVP_cleanup(); + os_free(tls_global->ocsp_stapling_response); + tls_global->ocsp_stapling_response = NULL; os_free(tls_global); tls_global = NULL; } @@ -806,16 +894,11 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); return -1; } - if (pin == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); - return -1; - } - if (key_id == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); - return -1; - } ERR_clear_error(); +#ifdef ANDROID + ENGINE_load_dynamic(); +#endif conn->engine = ENGINE_by_id(engine_id); if (!conn->engine) { wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", @@ -830,20 +913,35 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, } wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); - if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { +#ifndef ANDROID + if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", ERR_error_string(ERR_get_error(), NULL)); goto err; } - /* load private key first in-case PIN is required for cert */ - conn->private_key = ENGINE_load_private_key(conn->engine, - key_id, NULL, NULL); - if (!conn->private_key) { - wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" - " '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; - goto err; +#endif + if (key_id) { + /* + * Ensure that the ENGINE does not attempt to use the OpenSSL + * UI system to obtain a PIN, if we didn't provide one. + */ + struct { + const void *password; + const char *prompt_info; + } key_cb = { "", NULL }; + + /* load private key first in-case PIN is required for cert */ + conn->private_key = ENGINE_load_private_key(conn->engine, + key_id, NULL, + &key_cb); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(ERR_get_error(), NULL)); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } } /* handle a certificate and/or CA certificate */ @@ -910,15 +1008,41 @@ int tls_get_errors(void *ssl_ctx) return count; } + +static void tls_msg_cb(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg) +{ + struct tls_connection *conn = arg; + const u8 *pos = buf; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d", + write_p ? "TX" : "RX", version, content_type); + wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len); + if (content_type == 24 && len >= 3 && pos[0] == 1) { + size_t payload_len = WPA_GET_BE16(pos + 1); + if (payload_len + 3 > len) { + wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected"); + conn->invalid_hb_used = 1; + } + } +} + + struct tls_connection * tls_connection_init(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; long options; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_context *context = SSL_CTX_get_app_data(ssl); +#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + struct tls_context *context = tls_global; +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->ssl_ctx = ssl_ctx; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, @@ -927,7 +1051,10 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) return NULL; } + conn->context = context; SSL_set_app_data(conn->ssl, conn); + SSL_set_msg_callback(conn->ssl, tls_msg_cb); + SSL_set_msg_callback_arg(conn->ssl, conn); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; #ifdef SSL_OP_NO_COMPRESSION @@ -968,6 +1095,8 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); + os_free(conn->suffix_match); + os_free(conn->domain_match); os_free(conn->session_ticket); os_free(conn); } @@ -998,7 +1127,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, { GENERAL_NAME *gen; void *ext; - int i, found = 0; + int found = 0; + stack_index_t i; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); @@ -1058,6 +1188,112 @@ static int tls_match_altsubject(X509 *cert, const char *match) } +#ifndef CONFIG_NATIVE_WINDOWS +static int domain_suffix_match(const u8 *val, size_t len, const char *match, + int full) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len || (full && match_len != len)) + return 0; + + if (os_strncasecmp((const char *) val + len - match_len, match, + match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match, int full) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + GENERAL_NAME *gen; + void *ext; + int i; + stack_index_t j; + int dns_name = 0; + X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s", + full ? "": "suffix ", match); + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) { + gen = sk_GENERAL_NAME_value(ext, j); + if (gen->type != GEN_DNS) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->d.dNSName->data, + gen->d.dNSName->length); + if (domain_suffix_match(gen->d.dNSName->data, + gen->d.dNSName->length, match, full) == + 1) { + wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", + full ? "Match" : "Suffix match"); + return 1; + } + } + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = X509_get_subject_name(cert); + i = -1; + for (;;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + i = X509_NAME_get_index_by_NID(name, NID_commonName, i); + if (i == -1) + break; + e = X509_NAME_get_entry(name, i); + if (e == NULL) + continue; + cn = X509_NAME_ENTRY_get_data(e); + if (cn == NULL) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match, full) == 1) + { + wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", + full ? "Match" : "Suffix match"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", + full ? "": "suffix "); + return 0; +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + static enum tls_fail_reason openssl_tls_fail_reason(int err) { switch (err) { @@ -1122,8 +1358,9 @@ static void openssl_tls_fail_event(struct tls_connection *conn, { union tls_event_data ev; struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; cert = get_x509_cert(err_cert); @@ -1134,7 +1371,7 @@ static void openssl_tls_fail_event(struct tls_connection *conn, ev.cert_fail.subject = subject; ev.cert_fail.reason_txt = err_str; ev.cert_fail.cert = cert; - tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); wpabuf_free(cert); } @@ -1145,15 +1382,21 @@ static void openssl_tls_cert_event(struct tls_connection *conn, { struct wpabuf *cert = NULL; union tls_event_data ev; + struct tls_context *context = conn->context; + char *altsubject[TLS_MAX_ALT_SUBJECT]; + int alt, num_altsubject = 0; + GENERAL_NAME *gen; + void *ext; + stack_index_t i; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe || tls_global->cert_in_cb) { + if (conn->cert_probe || context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1171,8 +1414,52 @@ static void openssl_tls_cert_event(struct tls_connection *conn, #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; - tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + + ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL); + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + char *pos; + + if (num_altsubject == TLS_MAX_ALT_SUBJECT) + break; + gen = sk_GENERAL_NAME_value(ext, i); + if (gen->type != GEN_EMAIL && + gen->type != GEN_DNS && + gen->type != GEN_URI) + continue; + + pos = os_malloc(10 + gen->d.ia5->length + 1); + if (pos == NULL) + break; + altsubject[num_altsubject++] = pos; + + switch (gen->type) { + case GEN_EMAIL: + os_memcpy(pos, "EMAIL:", 6); + pos += 6; + break; + case GEN_DNS: + os_memcpy(pos, "DNS:", 4); + pos += 4; + break; + case GEN_URI: + os_memcpy(pos, "URI:", 4); + pos += 4; + break; + } + + os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length); + pos += gen->d.ia5->length; + *pos = '\0'; + } + + for (alt = 0; alt < num_altsubject; alt++) + ev.peer_cert.altsubject[alt] = altsubject[alt]; + ev.peer_cert.num_altsubject = num_altsubject; + + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); + for (alt = 0; alt < num_altsubject; alt++) + os_free(altsubject[alt]); } @@ -1183,10 +1470,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; - char *match, *altmatch; + struct tls_context *context; + char *match, *altmatch, *suffix_match, *domain_match; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + if (!err_cert) + return 0; + err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); ssl = X509_STORE_CTX_get_ex_data(x509_ctx, @@ -1196,8 +1487,19 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) conn = SSL_get_app_data(ssl); if (conn == NULL) return 0; + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + else if (depth == 2) + conn->peer_issuer_issuer = err_cert; + + context = conn->context; match = conn->subject_match; altmatch = conn->altsubject_match; + suffix_match = conn->suffix_match; + domain_match = conn->domain_match; if (!preverify_ok && !conn->ca_cert_verify) preverify_ok = 1; @@ -1214,7 +1516,11 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) err_str = X509_verify_cert_error_string(err); #ifdef CONFIG_SHA256 - if (preverify_ok && depth == 0 && conn->server_cert_only) { + /* + * Do not require preverify_ok so we can explicity allow otherwise + * invalid pinned server certificates. + */ + if (depth == 0 && conn->server_cert_only) { struct wpabuf *cert; cert = get_x509_cert(err_cert); if (!cert) { @@ -1232,6 +1538,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) err_str = "Server certificate mismatch"; err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; preverify_ok = 0; + } else if (!preverify_ok) { + /* + * Certificate matches pinned certificate, allow + * regardless of other problems. + */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Ignore validation issues for a pinned server certificate"); + preverify_ok = 1; } wpabuf_free(cert); } @@ -1266,6 +1580,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) openssl_tls_fail_event(conn, err_cert, err, depth, buf, "AltSubject mismatch", TLS_FAIL_ALTSUBJECT_MISMATCH); + } else if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match, 0)) { + wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); + } else if (depth == 0 && domain_match && + !tls_match_suffix(err_cert, domain_match, 1)) { + wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", + domain_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); } else openssl_tls_cert_event(conn, err_cert, depth, buf); @@ -1278,9 +1608,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } - if (preverify_ok && tls_global->event_cb != NULL) - tls_global->event_cb(tls_global->cb_ctx, - TLS_CERT_CHAIN_SUCCESS, NULL); + if (preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); return preverify_ok; } @@ -1293,7 +1623,7 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) X509_LOOKUP *lookup; int ret = 0; - lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx), X509_LOOKUP_file()); if (lookup == NULL) { tls_show_errors(MSG_WARNING, __func__, @@ -1319,36 +1649,24 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ -#ifdef ANDROID -static BIO * BIO_from_keystore(const char *key) -{ - BIO *bio = NULL; - char value[KEYSTORE_MESSAGE_SIZE]; - int length = keystore_get(key, strlen(key), value); - if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) - BIO_write(bio, value, length); - return bio; -} -#endif /* ANDROID */ - - static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, const char *ca_cert, const u8 *ca_cert_blob, size_t ca_cert_blob_len, const char *ca_path) { SSL_CTX *ssl_ctx = _ssl_ctx; + X509_STORE *store; /* * Remove previously configured trusted CA certificates before adding * new ones. */ - X509_STORE_free(ssl_ctx->cert_store); - ssl_ctx->cert_store = X509_STORE_new(); - if (ssl_ctx->cert_store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); return -1; } + SSL_CTX_set_cert_store(ssl_ctx, store); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); conn->ca_cert_verify = 1; @@ -1392,7 +1710,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } if (ca_cert_blob) { - X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + X509 *cert = d2i_X509(NULL, + (const unsigned char **) &ca_cert_blob, ca_cert_blob_len); if (cert == NULL) { tls_show_errors(MSG_WARNING, __func__, @@ -1400,7 +1719,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return -1; } - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert_blob to " @@ -1426,7 +1746,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { BIO *bio = BIO_from_keystore(&ca_cert[11]); STACK_OF(X509_INFO) *stack = NULL; - int i; + stack_index_t i; if (bio) { stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); @@ -1541,7 +1861,9 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, - const char *altsubject_match) + const char *altsubject_match, + const char *suffix_match, + const char *domain_match) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -1559,6 +1881,22 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (conn->suffix_match == NULL) + return -1; + } + + os_free(conn->domain_match); + conn->domain_match = NULL; + if (domain_match) { + conn->domain_match = os_strdup(domain_match); + if (conn->domain_match == NULL) + return -1; + } + return 0; } @@ -1813,7 +2151,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, #ifdef PKCS12_FUNCS PKCS12 *p12; - p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len); if (p12 == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to use PKCS#12 blob"); @@ -1894,20 +2232,21 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, #ifndef OPENSSL_NO_ENGINE X509 *cert; SSL_CTX *ssl_ctx = _ssl_ctx; + X509_STORE *store; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) return -1; /* start off the same as tls_connection_ca_cert */ - X509_STORE_free(ssl_ctx->cert_store); - ssl_ctx->cert_store = X509_STORE_new(); - if (ssl_ctx->cert_store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); X509_free(cert); return -1; } - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + SSL_CTX_set_cert_store(ssl_ctx, store); + if (!X509_STORE_add_cert(store, cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add CA certificate from engine " @@ -2022,26 +2361,6 @@ static int tls_connection_private_key(void *_ssl_ctx, break; } -#ifdef ANDROID - if (!ok && private_key && - os_strncmp("keystore://", private_key, 11) == 0) { - BIO *bio = BIO_from_keystore(&private_key[11]); - EVP_PKEY *pkey = NULL; - if (bio) { - pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (pkey) { - if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: Private key " - "from keystore"); - ok = 1; - } - EVP_PKEY_free(pkey); - } - } -#endif /* ANDROID */ - while (!ok && private_key) { #ifndef OPENSSL_NO_STDIO if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -2463,10 +2782,25 @@ openssl_connection_handshake(struct tls_connection *conn, out_data = openssl_handshake(conn, in_data, server); if (out_data == NULL) return NULL; + if (conn->invalid_hb_used) { + wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); + wpabuf_free(out_data); + return NULL; + } if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + if (conn->invalid_hb_used) { + wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); + if (appl_data) { + wpabuf_free(*appl_data); + *appl_data = NULL; + } + wpabuf_free(out_data); + return NULL; + } + return out_data; } @@ -2568,13 +2902,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, } wpabuf_put(buf, res); + if (conn->invalid_hb_used) { + wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); + wpabuf_free(buf); + return NULL; + } + return buf; } int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + return conn ? SSL_cache_hit(conn->ssl) : 0; +#else return conn ? conn->ssl->hit : 0; +#endif } @@ -2615,7 +2959,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, return -1; } ret = os_snprintf(pos, end - pos, ":%s", suite); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; @@ -2670,15 +3014,9 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || conn->ssl == NULL || ext_type != 35) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE if (SSL_set_session_ticket_ext(conn->ssl, (void *) data, data_len) != 1) return -1; -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ - if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, - data_len) != 1) - return -1; -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ return 0; } @@ -2709,36 +3047,332 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + OCSP_RESPONSE_print(out, rsp, 0); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt); + } + os_free(txt); + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void debug_print_cert(X509 *cert, const char *title) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + X509_print(out, cert); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt); + } + os_free(txt); + + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int ocsp_resp_cb(SSL *s, void *arg) +{ + struct tls_connection *conn = arg; + const unsigned char *p; + int len, status, reason; + OCSP_RESPONSE *rsp; + OCSP_BASICRESP *basic; + OCSP_CERTID *id; + ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; + X509_STORE *store; + STACK_OF(X509) *certs = NULL; + + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + if (!p) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); + return 0; + } + + ocsp_debug_print_resp(rsp); + + status = OCSP_response_status(rsp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", + status, OCSP_response_status_str(status)); + return 0; + } + + basic = OCSP_response_get1_basic(rsp); + if (!basic) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); + return 0; + } + + store = SSL_CTX_get_cert_store(conn->ssl_ctx); + if (conn->peer_issuer) { + debug_print_cert(conn->peer_issuer, "Add OCSP issuer"); + + if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store"); + } + certs = sk_X509_new_null(); + if (certs) { + X509 *cert; + cert = X509_dup(conn->peer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store"); + X509_free(cert); + sk_X509_free(certs); + certs = NULL; + } + if (certs && conn->peer_issuer_issuer) { + cert = X509_dup(conn->peer_issuer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); + X509_free(cert); + } + } + } + } + + status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); + sk_X509_pop_free(certs, X509_free); + if (status <= 0) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP response failed verification"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); + + if (!conn->peer_cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update)) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", + (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : + " (OCSP not required)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP status times invalid"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", + OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + return 1; + if (status == V_OCSP_CERTSTATUS_REVOKED) + return 0; + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); + return 0; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + return 1; +} + + +static int ocsp_status_cb(SSL *s, void *arg) +{ + char *tmp; + char *resp; + size_t len; + + if (tls_global->ocsp_stapling_response == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured"); + return SSL_TLSEXT_ERR_OK; + } + + resp = os_readfile(tls_global->ocsp_stapling_response, &len); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file"); + /* TODO: Build OCSPResponse with responseStatus = internalError + */ + return SSL_TLSEXT_ERR_OK; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response"); + tmp = OPENSSL_malloc(len); + if (tmp == NULL) { + os_free(resp); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + os_memcpy(tmp, resp, len); + os_free(resp); + SSL_set_tlsext_status_ocsp_resp(s, tmp, len); + + return SSL_TLSEXT_ERR_OK; +} + +#endif /* HAVE_OCSP */ + + int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; unsigned long err; + int can_pkcs11 = 0; + const char *key_id = params->key_id; + const char *cert_id = params->cert_id; + const char *ca_cert_id = params->ca_cert_id; + const char *engine_id = params->engine ? params->engine_id : NULL; if (conn == NULL) return -1; + /* + * If the engine isn't explicitly configured, and any of the + * cert/key fields are actually PKCS#11 URIs, then automatically + * use the PKCS#11 ENGINE. + */ + if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0) + can_pkcs11 = 1; + + if (!key_id && params->private_key && can_pkcs11 && + os_strncmp(params->private_key, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + key_id = params->private_key; + } + + if (!cert_id && params->client_cert && can_pkcs11 && + os_strncmp(params->client_cert, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + cert_id = params->client_cert; + } + + if (!ca_cert_id && params->ca_cert && can_pkcs11 && + os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + ca_cert_id = params->ca_cert; + } + + /* If we need to automatically enable the PKCS#11 ENGINE, do so. */ + if (can_pkcs11 == 2 && !engine_id) + engine_id = "pkcs11"; + + if (params->flags & TLS_CONN_EAP_FAST) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Use TLSv1_method() for EAP-FAST"); + if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Failed to set TLSv1_method() for EAP-FAST"); + return -1; + } + } + while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", __func__, ERR_error_string(err, NULL)); } - if (params->engine) { + if (engine_id) { wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); - ret = tls_engine_init(conn, params->engine_id, params->pin, - params->key_id, params->cert_id, - params->ca_cert_id); + ret = tls_engine_init(conn, engine_id, params->pin, + key_id, cert_id, ca_cert_id); if (ret) return ret; } if (tls_connection_set_subject_match(conn, params->subject_match, - params->altsubject_match)) + params->altsubject_match, + params->suffix_match, + params->domain_match)) return -1; - if (params->engine && params->ca_cert_id) { + if (engine_id && ca_cert_id) { if (tls_connection_engine_ca_cert(tls_ctx, conn, - params->ca_cert_id)) + ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, params->ca_cert_blob, @@ -2746,15 +3380,15 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->ca_path)) return -1; - if (params->engine && params->cert_id) { - if (tls_connection_engine_client_cert(conn, params->cert_id)) + if (engine_id && cert_id) { + if (tls_connection_engine_client_cert(conn, cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_client_cert(conn, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) return -1; - if (params->engine && params->key_id) { + if (engine_id && key_id) { wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; @@ -2774,13 +3408,45 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ciphers && + SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options else SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ +#ifdef SSL_OP_NO_TLSv1_1 + if (params->flags & TLS_CONN_DISABLE_TLSv1_1) + SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1); + else + SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1); +#endif /* SSL_OP_NO_TLSv1_1 */ +#ifdef SSL_OP_NO_TLSv1_2 + if (params->flags & TLS_CONN_DISABLE_TLSv1_2) + SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2); + else + SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2); +#endif /* SSL_OP_NO_TLSv1_2 */ + +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_CTX *ssl_ctx = tls_ctx; + SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); + } +#endif /* HAVE_OCSP */ + conn->flags = params->flags; tls_get_errors(tls_ctx); @@ -2816,13 +3482,34 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->openssl_ciphers && + SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); +#ifdef SSL_CTX_clear_options else SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ +#ifdef HAVE_OCSP + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx); + os_free(tls_global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + tls_global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + tls_global->ocsp_stapling_response = NULL; +#endif /* HAVE_OCSP */ + return 0; } @@ -2875,9 +3562,15 @@ unsigned int tls_capabilities(void *tls_ctx) * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ +#ifdef OPENSSL_IS_BORINGSSL +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + const SSL_CIPHER **cipher, void *arg) +#else /* OPENSSL_IS_BORINGSSL */ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg) +#endif /* OPENSSL_IS_BORINGSSL */ { struct tls_connection *conn = arg; int ret; @@ -2901,7 +3594,6 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, } -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, int len, void *arg) { @@ -2927,62 +3619,6 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, return 1; } -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET -static void tls_hello_ext_cb(SSL *s, int client_server, int type, - unsigned char *data, int len, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->session_ticket_cb == NULL) - return; - - wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, - type, len); - - if (type == TLSEXT_TYPE_session_ticket && !client_server) { - os_free(conn->session_ticket); - conn->session_ticket = NULL; - - wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " - "extension", data, len); - conn->session_ticket = os_malloc(len); - if (conn->session_ticket == NULL) - return; - - os_memcpy(conn->session_ticket, data, len); - conn->session_ticket_len = len; - } -} -#else /* SSL_OP_NO_TICKET */ -static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->session_ticket_cb == NULL) - return 0; - - wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, - ext->type, ext->length); - - os_free(conn->session_ticket); - conn->session_ticket = NULL; - - if (ext->type == 35) { - wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " - "extension", ext->data, ext->length); - conn->session_ticket = os_malloc(ext->length); - if (conn->session_ticket == NULL) - return SSL_AD_INTERNAL_ERROR; - - os_memcpy(conn->session_ticket, ext->data, ext->length); - conn->session_ticket_len = ext->length; - } - - return 0; -} -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ @@ -2999,33 +3635,12 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, conn) != 1) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, tls_session_ticket_ext_cb, conn); -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET - SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb); - SSL_set_tlsext_debug_arg(conn->ssl, conn); -#else /* SSL_OP_NO_TICKET */ - if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb, - conn) != 1) - return -1; -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } else { if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL); -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET - SSL_set_tlsext_debug_callback(conn->ssl, NULL); - SSL_set_tlsext_debug_arg(conn->ssl, conn); -#else /* SSL_OP_NO_TICKET */ - if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1) - return -1; -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } return 0; @@ -3033,3 +3648,11 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, return -1; #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ } + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", + OPENSSL_VERSION_TEXT, + SSLeay_version(SSLEAY_VERSION)); +} diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c index 2c2daa8a804b..31a2c946d047 100644 --- a/src/crypto/tls_schannel.c +++ b/src/crypto/tls_schannel.c @@ -692,6 +692,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL) return -1; + if (params->subject_match) { + wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + return -1; + } + + if (params->altsubject_match) { + wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + return -1; + } + + if (params->suffix_match) { + wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + return -1; + } + + if (params->domain_match) { + wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + return -1; + } + + if (params->openssl_ciphers) { + wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); + return -1; + } + if (global->my_cert_store == NULL && (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == NULL) { @@ -730,3 +755,9 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "schannel"); +} diff --git a/src/drivers/Makefile b/src/drivers/Makefile index 07600e52c2fd..5721154014ec 100644 --- a/src/drivers/Makefile +++ b/src/drivers/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov rm -f build.wpa_supplicant build.hostapd install: diff --git a/src/drivers/android_drv.h b/src/drivers/android_drv.h index 5906527a017e..31d94408a92f 100644 --- a/src/drivers/android_drv.h +++ b/src/drivers/android_drv.h @@ -1,12 +1,8 @@ /* * Android driver interface * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ANDROID_DRV_H diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 7ee71aaac057..03bd1a79a14c 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1,6 +1,6 @@ /* * Driver interface definition - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -20,14 +20,51 @@ #define WPA_SUPPLICANT_DRIVER_VERSION 4 #include "common/defs.h" +#include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 -#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 -#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#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 @@ -49,12 +86,38 @@ struct hostapd_channel_data { int flag; /** - * max_tx_power - maximum transmit power in dBm + * 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 @@ -117,16 +180,24 @@ struct hostapd_hw_modes { #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_AUTHENTICATED BIT(4) #define WPA_SCAN_ASSOCIATED BIT(5) /** @@ -142,6 +213,9 @@ struct hostapd_hw_modes { * @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 * @@ -153,6 +227,11 @@ struct hostapd_hw_modes { * 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; @@ -165,25 +244,23 @@ struct wpa_scan_res { 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 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. - */ + /* 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; }; /** @@ -289,7 +366,51 @@ struct wpa_driver_scan_params { * Mbps from the support rates element(s) in the Probe Request frames * and not to transmit the frames at any of those rates. */ - u8 p2p_probe; + 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. + */ }; /** @@ -314,16 +435,96 @@ struct wpa_driver_auth_params { */ 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; - size_t sae_data_len; + /** + * 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_OPEN /* WPS provisioning with AP that is in open mode */, - WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection - */ + /** + * 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; }; /** @@ -337,6 +538,16 @@ struct wpa_driver_associate_params { * 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 */ @@ -348,11 +559,19 @@ struct wpa_driver_associate_params { size_t ssid_len; /** - * freq - Frequency of the channel the selected AP is using - * Frequency that the selected AP is using (in MHz as - * reported in the scan results) + * freq - channel parameters */ - int freq; + 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 @@ -361,6 +580,11 @@ struct wpa_driver_associate_params { */ 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 @@ -392,25 +616,25 @@ struct wpa_driver_associate_params { unsigned int wpa_proto; /** - * pairwise_suite - Selected pairwise cipher suite + * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ - enum wpa_cipher pairwise_suite; + unsigned int pairwise_suite; /** - * group_suite - Selected group cipher suite + * group_suite - Selected group cipher suite (WPA_CIPHER_*) * * This is usually ignored if @wpa_ie is used. */ - enum wpa_cipher group_suite; + unsigned int group_suite; /** - * key_mgmt_suite - Selected key management suite + * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * * This is usually ignored if @wpa_ie is used. */ - enum wpa_key_mgmt key_mgmt_suite; + unsigned int key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms @@ -547,18 +771,62 @@ struct wpa_driver_associate_params { */ 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; /** - * 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. + * 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 */ - const u8 *htcaps; /* struct ieee80211_ht_capabilities * */ - const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */ + 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 { @@ -567,11 +835,21 @@ enum hide_ssid { 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 */ - const u8 *head; + u8 *head; /** * head_len - Length of the head buffer in octets @@ -581,7 +859,7 @@ struct wpa_driver_ap_params { /** * tail - Beacon tail following TIM IE */ - const u8 *tail; + u8 *tail; /** * tail_len - Length of the tail buffer in octets @@ -612,7 +890,7 @@ struct wpa_driver_ap_params { * This is used by drivers that reply to Probe Requests internally in * AP mode and require the full Probe Response template. */ - const u8 *proberesp; + u8 *proberesp; /** * proberesp_len - Length of the proberesp buffer in octets @@ -744,10 +1022,65 @@ struct wpa_driver_ap_params { */ 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; }; /** @@ -762,6 +1095,9 @@ struct wpa_driver_capa { #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 @@ -770,83 +1106,132 @@ struct wpa_driver_capa { #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 */ +/** Driver generated WPA/RSN IE */ #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 -/* Driver needs static WEP key setup after association command */ +/** Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 -/* unused: 0x00000004 */ -/* Driver takes care of RSN 4-way handshake internally; PMK is configured with +/** 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 +/** Driver provides separate commands for authentication and association (SME in * wpa_supplicant). */ #define WPA_DRIVER_FLAGS_SME 0x00000020 -/* Driver supports AP mode */ +/** Driver supports AP mode */ #define WPA_DRIVER_FLAGS_AP 0x00000040 -/* Driver needs static WEP key setup after association has been completed */ +/** Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 -/* Driver takes care of P2P management operations */ -#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100 -/* Driver supports concurrent P2P operations */ +/** 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 Device, GO, or P2P Client */ +/** This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 -/* Driver supports concurrent operations on multiple channels */ -#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000 -/* +/** 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 */ +/** Driver supports off-channel TX */ #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 -/* Driver indicates TX status events for EAPOL Data frames */ +/** 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 */ +/** Driver indicates TX status events for Deauth/Disassoc frames */ #define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 -/* Driver supports roaming (BSS selection) in firmware */ +/** Driver supports roaming (BSS selection) in firmware */ #define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 -/* Driver supports operating as a TDLS peer */ +/** Driver supports operating as a TDLS peer */ #define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 -/* Driver requires external TDLS setup/teardown/discovery */ +/** 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 */ +/** 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 */ +/** Driver supports U-APSD in AP mode */ #define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 -/* Driver supports inactivity timer in AP mode */ +/** Driver supports inactivity timer in AP mode */ #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 -/* Driver expects user space implementation of MLME in AP mode */ +/** Driver expects user space implementation of MLME in AP mode */ #define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 -/* Driver supports SAE with user space SME */ +/** Driver supports SAE with user space SME */ #define WPA_DRIVER_FLAGS_SAE 0x02000000 -/* Driver makes use of OBSS scan mechanism in wpa_supplicant */ +/** Driver makes use of OBSS scan mechanism in wpa_supplicant */ #define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 - unsigned int flags; - +/** 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 + 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; /** @@ -864,15 +1249,51 @@ struct wpa_driver_capa { * probe_resp_offloads - Bitmap of supported protocols by the driver * for Probe Response offloading. */ -/* Driver Probe Response offloading support for WPS ver. 1 */ +/** 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 */ +/** Driver Probe Response offloading support for WPS ver. 2 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 -/* Driver Probe Response offloading support for P2P */ +/** 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) */ +/** 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; }; @@ -898,19 +1319,32 @@ struct hostapd_sta_add_params { 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 hostapd_freq_params { - int mode; - int freq; - int channel; - int ht_enabled; - int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, - * secondary channel below primary, 1 = HT40 - * enabled, secondary channel above primary */ +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 { @@ -948,16 +1382,25 @@ enum wpa_driver_if_type { * 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_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, }; struct wpa_init_params { void *global_priv; const u8 *bssid; const char *ifname; - const u8 *ssid; - size_t ssid_len; - const char *test_socket; + const char *driver_params; int use_pae_group_addr; char **bridge; size_t num_bridge; @@ -986,17 +1429,7 @@ struct wpa_bss_params { #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) - -/** - * struct p2p_params - P2P parameters for driver-based P2P management - */ -struct p2p_params { - const char *dev_name; - u8 pri_dev_type[8]; -#define DRV_MAX_SEC_DEV_TYPES 5 - u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8]; - size_t num_sec_dev_types; -}; +#define WPA_STA_AUTHENTICATED BIT(5) enum tdls_oper { TDLS_DISCOVERY_REQ, @@ -1025,6 +1458,17 @@ enum wnm_oper { 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 */ @@ -1032,10 +1476,120 @@ struct wpa_signal_info { u32 frequency; int above_threshold; int current_signal; + int avg_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, +}; + +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; }; + /** * struct wpa_driver_ops - Driver interface API definition * @@ -1085,7 +1639,9 @@ struct wpa_driver_ops { * @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, %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 @@ -1318,27 +1874,6 @@ struct wpa_driver_ops { * a MAC address. */ const u8 * (*get_mac_addr)(void *priv); - /** - * send_eapol - Optional function for sending EAPOL packets - * @priv: private driver interface data - * @dest: Destination MAC address - * @proto: Ethertype - * @data: EAPOL packet starting with IEEE 802.1X header - * @data_len: Size of the EAPOL packet - * - * Returns: 0 on success, -1 on failure - * - * This optional function can be used to override l2_packet operations - * with driver specific functionality. If this function pointer is set, - * l2_packet module is not used at all and the driver interface code is - * responsible for receiving and sending all EAPOL packets. The - * received EAPOL packets are sent to core code with EVENT_EAPOL_RX - * event. The driver interface is required to implement get_mac_addr() - * handler if send_eapol() is used. - */ - int (*send_eapol)(void *priv, const u8 *dest, u16 proto, - const u8 *data, size_t data_len); - /** * set_operstate - Sets device operating state to DORMANT or UP * @priv: private driver interface data @@ -1413,22 +1948,6 @@ struct wpa_driver_ops { int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies, size_t ies_len); - /** - * send_ft_action - Send FT Action frame (IEEE 802.11r) - * @priv: Private driver interface data - * @action: Action field value - * @target_ap: Target AP address - * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body) - * @ies_len: Length of FT IEs in bytes - * Returns: 0 on success, -1 on failure - * - * The supplicant uses this callback to request the driver to transmit - * an FT Action frame (action category 6) for over-the-DS fast BSS - * transition. - */ - int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len); - /** * get_scan_results2 - Fetch the latest scan results * @priv: private driver interface data @@ -1449,6 +1968,14 @@ struct wpa_driver_ops { */ 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 @@ -1542,6 +2069,16 @@ struct wpa_driver_ops { */ 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 @@ -1825,12 +2362,13 @@ struct wpa_driver_ops { * (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); + const char *bridge, int use_existing); /** * if_remove - Remove a virtual interface @@ -1892,7 +2430,7 @@ struct wpa_driver_ops { * @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, + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, u32 session_timeout); /** @@ -1951,10 +2489,12 @@ struct wpa_driver_ops { * @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); + const char *bridge_ifname, char *ifname_wds); /** * send_action - Transmit an Action frame @@ -2005,7 +2545,7 @@ struct wpa_driver_ops { * * 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_ACTION events. Optionally, 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. * @@ -2056,8 +2596,9 @@ struct wpa_driver_ops { * Returns: 0 on success, -1 on failure (or if not supported) * * This optional function can be used to disable AP mode related - * configuration and change the driver mode to station mode to allow - * normal station operations like scanning to be completed. + * 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); @@ -2066,8 +2607,9 @@ struct wpa_driver_ops { * @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. It - * can be used to change the interface type back to station mode. + * 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); @@ -2184,222 +2726,6 @@ struct wpa_driver_ops { */ const char * (*get_radio_name)(void *priv); - /** - * p2p_find - Start P2P Device Discovery - * @priv: Private driver interface data - * @timeout: Timeout for find operation in seconds or 0 for no timeout - * @type: Device Discovery type (enum p2p_discovery_type) - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_find)(void *priv, unsigned int timeout, int type); - - /** - * p2p_stop_find - Stop P2P Device Discovery - * @priv: Private driver interface data - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_stop_find)(void *priv); - - /** - * p2p_listen - Start P2P Listen state for specified duration - * @priv: Private driver interface data - * @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. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_listen)(void *priv, unsigned int timeout); - - /** - * p2p_connect - Start P2P group formation (GO negotiation) - * @priv: Private driver interface data - * @peer_addr: MAC address of the peer P2P client - * @wps_method: enum p2p_wps_method value indicating config method - * @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 persistent group - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method, - int go_intent, const u8 *own_interface_addr, - unsigned int force_freq, int persistent_group); - - /** - * wps_success_cb - Report successfully completed WPS provisioning - * @priv: Private driver interface data - * @peer_addr: Peer address - * Returns: 0 on success, -1 on failure - * - * This function is used to report successfully completed WPS - * provisioning during group formation in both GO/Registrar and - * client/Enrollee roles. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*wps_success_cb)(void *priv, const u8 *peer_addr); - - /** - * p2p_group_formation_failed - Report failed WPS provisioning - * @priv: Private driver interface data - * Returns: 0 on success, -1 on failure - * - * 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. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_group_formation_failed)(void *priv); - - /** - * p2p_set_params - Set P2P parameters - * @priv: Private driver interface data - * @params: P2P parameters - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_set_params)(void *priv, const struct p2p_params *params); - - /** - * p2p_prov_disc_req - Send Provision Discovery Request - * @priv: Private driver interface data - * @peer_addr: MAC address of the peer P2P client - * @config_methods: WPS Config Methods value (only one bit set) - * 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 EVENT_P2P_PROV_DISC_RESPONSE event. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr, - u16 config_methods, int join); - - /** - * p2p_sd_request - Schedule a service discovery query - * @priv: Private driver interface data - * @dst: Destination peer or %NULL to apply for all peers - * @tlvs: P2P Service Query TLV(s) - * Returns: Reference to the query or 0 on failure - * - * Response to the query is indicated with the - * EVENT_P2P_SD_RESPONSE driver event. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - u64 (*p2p_sd_request)(void *priv, const u8 *dst, - const struct wpabuf *tlvs); - - /** - * p2p_sd_cancel_request - Cancel a pending service discovery query - * @priv: Private driver interface data - * @req: Query reference from p2p_sd_request() - * Returns: 0 on success, -1 on failure - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_sd_cancel_request)(void *priv, u64 req); - - /** - * p2p_sd_response - Send response to a service discovery query - * @priv: Private driver interface data - * @freq: Frequency from EVENT_P2P_SD_REQUEST event - * @dst: Destination address from EVENT_P2P_SD_REQUEST event - * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event - * @resp_tlvs: P2P Service Response TLV(s) - * Returns: 0 on success, -1 on failure - * - * This function is called as a response to the request indicated with - * the EVENT_P2P_SD_REQUEST driver event. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_sd_response)(void *priv, int freq, const u8 *dst, - u8 dialog_token, - const struct wpabuf *resp_tlvs); - - /** - * p2p_service_update - Indicate a change in local services - * @priv: Private driver interface data - * Returns: 0 on success, -1 on failure - * - * 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. - * - * This function is only used if the driver implements P2P management, - * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in - * struct wpa_driver_capa. - */ - int (*p2p_service_update)(void *priv); - - /** - * p2p_reject - Reject peer device (explicitly block connections) - * @priv: Private driver interface data - * @addr: MAC address of the peer - * Returns: 0 on success, -1 on failure - */ - int (*p2p_reject)(void *priv, const u8 *addr); - - /** - * p2p_invite - Invite a P2P Device into a group - * @priv: Private driver interface data - * @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 - * @go_dev_addr: Forced GO Device Address or %NULL if none - * @persistent_group: Whether this is to reinvoke a persistent group - * Returns: 0 on success, -1 on failure - */ - int (*p2p_invite)(void *priv, const u8 *peer, int role, - const u8 *bssid, const u8 *ssid, size_t ssid_len, - const u8 *go_dev_addr, int persistent_group); - /** * send_tdls_mgmt - for sending TDLS management packets * @priv: private driver interface data @@ -2407,6 +2733,8 @@ struct wpa_driver_ops { * @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 @@ -2415,8 +2743,8 @@ struct wpa_driver_ops { * 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, - const u8 *buf, size_t len); + 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 @@ -2442,11 +2770,66 @@ struct wpa_driver_ops { 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); /** @@ -2463,18 +2846,57 @@ struct wpa_driver_ops { */ 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, const u8 *kck, + void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, const u8 *replay_ctr); /** @@ -2595,13 +3017,370 @@ struct wpa_driver_ops { * switch_channel - Announce channel switch and migrate the GO to the * given frequency * @priv: Private driver interface data - * @freq: Frequency in MHz + * @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, unsigned int freq); + 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); }; @@ -2789,11 +3568,6 @@ enum wpa_event_type { */ EVENT_ASSOC_TIMED_OUT, - /** - * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received - */ - EVENT_FT_RRB_RX, - /** * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS */ @@ -2814,15 +3588,6 @@ enum wpa_event_type { */ EVENT_RX_MGMT, - /** - * EVENT_RX_ACTION - Action frame received - * - * This event is used to indicate when an Action frame has been - * received. Information about the received frame is included in - * union wpa_event_data::rx_action. - */ - EVENT_RX_ACTION, - /** * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started * @@ -2842,13 +3607,6 @@ enum wpa_event_type { */ EVENT_CANCEL_REMAIN_ON_CHANNEL, - /** - * EVENT_MLME_RX - Report reception of frame for MLME (test use only) - * - * This event is used only by driver_test.c and userspace MLME. - */ - EVENT_MLME_RX, - /** * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame * @@ -2877,9 +3635,7 @@ enum wpa_event_type { * 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. With - * %wpa_supplicant, this event is used only if the send_eapol() handler - * is used to override the use of l2_packet for EAPOL frame TX. + * deliver the receive EAPOL frames from the driver. */ EVENT_EAPOL_RX, @@ -2927,7 +3683,8 @@ enum wpa_event_type { * 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. + * interfaces. This event can be propagated when channel switching + * fails. */ EVENT_INTERFACE_UNAVAILABLE, @@ -2969,38 +3726,6 @@ enum wpa_event_type { */ EVENT_STATION_LOW_ACK, - /** - * EVENT_P2P_DEV_FOUND - Report a discovered P2P device - * - * This event is used only if the driver implements P2P management - * internally. Event data is stored in - * union wpa_event_data::p2p_dev_found. - */ - EVENT_P2P_DEV_FOUND, - - /** - * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request - * - * This event is used only if the driver implements P2P management - * internally. Event data is stored in - * union wpa_event_data::p2p_go_neg_req_rx. - */ - EVENT_P2P_GO_NEG_REQ_RX, - - /** - * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation - * - * This event is used only if the driver implements P2P management - * internally. Event data is stored in - * union wpa_event_data::p2p_go_neg_completed. - */ - EVENT_P2P_GO_NEG_COMPLETED, - - EVENT_P2P_PROV_DISC_REQUEST, - EVENT_P2P_PROV_DISC_RESPONSE, - EVENT_P2P_SD_REQUEST, - EVENT_P2P_SD_RESPONSE, - /** * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore */ @@ -3046,9 +3771,136 @@ enum wpa_event_type { * * This event can be used to request a WNM operation to be performed. */ - EVENT_WNM + 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 @@ -3131,10 +3983,63 @@ union wpa_event_data { */ 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; /** @@ -3243,7 +4148,8 @@ union wpa_event_data { u8 peer[ETH_ALEN]; enum { TDLS_REQUEST_SETUP, - TDLS_REQUEST_TEARDOWN + TDLS_REQUEST_TEARDOWN, + TDLS_REQUEST_DISCOVER, } oper; u16 reason_code; /* for teardown */ } tdls; @@ -3343,15 +4249,6 @@ union wpa_event_data { u8 addr[ETH_ALEN]; } timeout_event; - /** - * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events - */ - struct ft_rrb_rx { - const u8 *src; - const u8 *data; - size_t data_len; - } ft_rrb_rx; - /** * struct tx_status - Data for EVENT_TX_STATUS events */ @@ -3380,48 +4277,26 @@ union wpa_event_data { const u8 *frame; size_t frame_len; u32 datarate; - int ssi_signal; /* dBm */ - } rx_mgmt; - /** - * struct rx_action - Data for EVENT_RX_ACTION events - */ - struct rx_action { /** - * da - Destination address of the received Action frame - */ - const u8 *da; - - /** - * sa - Source address of the received Action frame - */ - const u8 *sa; - - /** - * bssid - Address 3 of the received Action frame - */ - const u8 *bssid; - - /** - * category - Action frame category - */ - u8 category; - - /** - * data - Action frame body after category field + * 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. */ - const u8 *data; + void *drv_priv; /** - * len - Length of data in octets + * freq - Frequency (in MHz) on which the frame was received */ - size_t len; + int freq; /** - * freq - Frequency (in MHz) on which the frame was received + * ssi_signal - Signal strength in dBm (or 0 if not available) */ - int freq; - } rx_action; + int ssi_signal; + } rx_mgmt; /** * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events @@ -3457,17 +4332,6 @@ union wpa_event_data { size_t num_ssids; } scan_info; - /** - * struct mlme_rx - Data for EVENT_MLME_RX events - */ - struct mlme_rx { - const u8 *buf; - size_t len; - int freq; - int channel; - int ssi; - } mlme_rx; - /** * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events */ @@ -3560,66 +4424,6 @@ union wpa_event_data { u8 addr[ETH_ALEN]; } low_ack; - /** - * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND - */ - struct p2p_dev_found { - const u8 *addr; - const u8 *dev_addr; - const u8 *pri_dev_type; - const char *dev_name; - u16 config_methods; - u8 dev_capab; - u8 group_capab; - } p2p_dev_found; - - /** - * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX - */ - struct p2p_go_neg_req_rx { - const u8 *src; - u16 dev_passwd_id; - } p2p_go_neg_req_rx; - - /** - * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED - */ - struct p2p_go_neg_completed { - struct p2p_go_neg_results *res; - } p2p_go_neg_completed; - - struct p2p_prov_disc_req { - 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; - } p2p_prov_disc_req; - - struct p2p_prov_disc_resp { - const u8 *peer; - u16 config_methods; - } p2p_prov_disc_resp; - - struct p2p_sd_req { - int freq; - const u8 *sa; - u8 dialog_token; - u16 update_indic; - const u8 *tlvs; - size_t tlvs_len; - } p2p_sd_req; - - struct p2p_sd_resp { - const u8 *sa; - u16 update_indic; - const u8 *tlvs; - size_t tlvs_len; - } p2p_sd_resp; - /** * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST */ @@ -3665,12 +4469,99 @@ union wpa_event_data { * @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 + */ + struct acs_selected_channels { + u8 pri_channel; + u8 sec_channel; + } acs_selected_channels; }; /** @@ -3729,4 +4620,17 @@ 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 struct wpa_driver_ops *wpa_drivers[]; + #endif /* DRIVER_H */ diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index c2f59344af5a..f46442163d93 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -224,10 +224,10 @@ set80211param(struct atheros_driver_data *drv, int op, int arg) memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { - perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); - wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d " - "(%s) arg %d)", __func__, drv->iface, op, - athr_get_param_name(op), arg); + wpa_printf(MSG_INFO, + "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s", + __func__, drv->iface, op, athr_get_param_name(op), + arg, strerror(errno)); return -1; } return 0; @@ -260,6 +260,17 @@ atheros_configure_wpa(struct atheros_driver_data *drv, case WPA_CIPHER_CCMP: v = IEEE80211_CIPHER_AES_CCM; break; +#ifdef ATH_GCM_SUPPORT + case WPA_CIPHER_CCMP_256: + v = IEEE80211_CIPHER_AES_CCM_256; + break; + case WPA_CIPHER_GCMP: + v = IEEE80211_CIPHER_AES_GCM; + break; + case WPA_CIPHER_GCMP_256: + v = IEEE80211_CIPHER_AES_GCM_256; + break; +#endif /* ATH_GCM_SUPPORT */ case WPA_CIPHER_TKIP: v = IEEE80211_CIPHER_TKIP; break; @@ -279,14 +290,15 @@ atheros_configure_wpa(struct atheros_driver_data *drv, } wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { - printf("Unable to set group key cipher to %u\n", v); + wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", 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(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { - printf("Unable to set group key length to %u\n", v); + wpa_printf(MSG_INFO, + "Unable to set group key length to %u", v); return -1; } } @@ -294,13 +306,22 @@ atheros_configure_wpa(struct atheros_driver_data *drv, v = 0; if (params->wpa_pairwise & WPA_CIPHER_CCMP) v |= 1<wpa_pairwise & WPA_CIPHER_CCMP_256) + v |= 1<wpa_pairwise & WPA_CIPHER_GCMP) + v |= 1<wpa_pairwise & WPA_CIPHER_GCMP_256) + v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) v |= 1<wpa_pairwise & WPA_CIPHER_NONE) v |= 1<wpa_key_mgmt); if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, params->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - params->wpa_key_mgmt); + wpa_printf(MSG_INFO, + "Unable to set key management algorithms to 0x%x", + params->wpa_key_mgmt); return -1; } @@ -326,13 +348,14 @@ atheros_configure_wpa(struct atheros_driver_data *drv, wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { - printf("Unable to set RSN capabilities to 0x%x\n", v); + wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x", + v); return -1; } wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { - printf("Unable to set WPA to %u\n", params->wpa); + wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); return -1; } return 0; @@ -354,19 +377,16 @@ atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) return atheros_set_privacy(drv, 0); } if (!params->wpa && !params->ieee802_1x) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); return -1; } if (params->wpa && atheros_configure_wpa(drv, params) != 0) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + wpa_printf(MSG_WARNING, "Error configuring WPA state!"); return -1; } if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); return -1; } @@ -474,20 +494,42 @@ atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, case WPA_ALG_CCMP: cipher = IEEE80211_CIPHER_AES_CCM; break; +#ifdef ATH_GCM_SUPPORT + case WPA_ALG_CCMP_256: + cipher = IEEE80211_CIPHER_AES_CCM_256; + break; + case WPA_ALG_GCMP: + cipher = IEEE80211_CIPHER_AES_GCM; + break; + case WPA_ALG_GCMP_256: + cipher = IEEE80211_CIPHER_AES_GCM_256; + break; +#endif /* ATH_GCM_SUPPORT */ #ifdef CONFIG_IEEE80211W case WPA_ALG_IGTK: cipher = IEEE80211_CIPHER_AES_CMAC; break; +#ifdef ATH_GCM_SUPPORT + case WPA_ALG_BIP_CMAC_256: + cipher = IEEE80211_CIPHER_AES_CMAC_256; + break; + case WPA_ALG_BIP_GMAC_128: + cipher = IEEE80211_CIPHER_AES_GMAC; + break; + case WPA_ALG_BIP_GMAC_256: + cipher = IEEE80211_CIPHER_AES_GMAC_256; + break; +#endif /* ATH_GCM_SUPPORT */ #endif /* CONFIG_IEEE80211W */ default: - printf("%s: unknown/unsupported algorithm %d\n", - __func__, alg); + wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d", + __func__, alg); return -1; } if (key_len > sizeof(wk.ik_keydata)) { - printf("%s: key length %lu too big\n", __func__, - (unsigned long) key_len); + wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__, + (unsigned long) key_len); return -3; } @@ -598,7 +640,8 @@ atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, return 0; } - printf("Failed to get station stats information element.\n"); + wpa_printf(MSG_INFO, + "Failed to get station stats information element"); return -1; } @@ -731,97 +774,125 @@ atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, return ret; } -#ifdef CONFIG_WPS -static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, + u8 qos_map_set_len) { +#ifdef CONFIG_ATHEROS_QOS_MAP struct atheros_driver_data *drv = ctx; - const struct ieee80211_mgmt *mgmt; - u16 fc; - union wpa_event_data event; + struct ieee80211req_athdbg req; + struct ieee80211_qos_map *qos_map = &req.data.qos_map; + struct iwreq iwr; + int i, up_start; - /* Send Probe Request information to WPS processing */ + if (qos_map_set_len < 16 || qos_map_set_len > 58 || + qos_map_set_len & 1) { + wpa_printf(MSG_ERROR, "Invalid QoS Map"); + return -1; + } else { + memset(&req, 0, sizeof(struct ieee80211req_athdbg)); + req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); + iwr.u.data.pointer = (void *) &req; + iwr.u.data.length = sizeof(struct ieee80211req_athdbg); + } - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) - return; - mgmt = (const struct ieee80211_mgmt *) buf; + qos_map->valid = 1; + qos_map->num_dscp_except = (qos_map_set_len - 16) / 2; + if (qos_map->num_dscp_except) { + for (i = 0; i < qos_map->num_dscp_except; i++) { + qos_map->dscp_exception[i].dscp = qos_map_set[i * 2]; + qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1]; + } + } - fc = le_to_host16(mgmt->frame_control); - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || - WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) - return; + up_start = qos_map_set_len - 16; + for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) { + qos_map->up[i].low = qos_map_set[up_start + (i * 2)]; + qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1]; + } - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { + wpa_printf(MSG_ERROR, + "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s", + __func__, drv->iface, strerror(errno)); + return -1; + } +#endif /* CONFIG_ATHEROS_QOS_MAP */ + + return 0; } -#endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R -static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20) +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct atheros_driver_data *drv = ctx; - union wpa_event_data event; const struct ieee80211_mgmt *mgmt; - u16 fc; - u16 stype; + union wpa_event_data event; + u16 fc, stype; int ielen; const u8 *iebuf; - /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */ if (len < IEEE80211_HDRLEN) return; + mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) return; + stype = WLAN_FC_GET_STYPE(fc); wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, (int) len); + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; + event.rx_probe_req.ie = mgmt->u.probe_req.variable; + event.rx_probe_req.ie_len = + len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); + return; + } + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", __func__); return; } + switch (stype) { case WLAN_FC_STYPE_ASSOC_REQ: - if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req)) + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)) break; ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); iebuf = mgmt->u.assoc_req.variable; drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); break; case WLAN_FC_STYPE_REASSOC_REQ: - if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req)) + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)) break; ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); iebuf = mgmt->u.reassoc_req.variable; drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); break; case WLAN_FC_STYPE_ACTION: - if (&mgmt->u.action.category > buf + len) - break; os_memset(&event, 0, sizeof(event)); - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category; - event.rx_action.len = buf + len - event.rx_action.data; - wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); break; case WLAN_FC_STYPE_AUTH: - if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth)) + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) break; os_memset(&event, 0, sizeof(event)); os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); @@ -840,107 +911,7 @@ static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, break; } } -#endif /* CONFIG_IEEE80211R */ - -#ifdef CONFIG_HS20 -static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct atheros_driver_data *drv = ctx; - const struct ieee80211_mgmt *mgmt; - u16 fc; - union wpa_event_data event; - - /* Send the Action frame for HS20 processing */ - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + - sizeof(mgmt->u.action.u.public_action)) - return; - - mgmt = (const struct ieee80211_mgmt *) buf; - - fc = le_to_host16(mgmt->frame_control); - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || - WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION || - mgmt->u.action.category != WLAN_ACTION_PUBLIC) - return; - - wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); - - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = (const u8 *) mgmt; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); -} -#endif /* CONFIG_HS20 */ - -#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) -static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct atheros_driver_data *drv = ctx; - union wpa_event_data event; - const struct ieee80211_mgmt *mgmt; - u16 fc; - u16 stype; - - /* Do 11R processing for WNM ACTION frames */ - if (len < IEEE80211_HDRLEN) - return; - mgmt = (const struct ieee80211_mgmt *) buf; - - fc = le_to_host16(mgmt->frame_control); - - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) - return; - stype = WLAN_FC_GET_STYPE(fc); - - wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, - (int) len); - - if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", - __func__); - return; - } - - switch (stype) { - case WLAN_FC_STYPE_ACTION: - if (&mgmt->u.action.category > buf + len) - break; - os_memset(&event, 0, sizeof(event)); - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category; - event.rx_action.len = buf + len - event.rx_action.data; - wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event); - break; - default: - break; - } -} -#endif /* CONFIG_WNM */ - -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) -static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ -#ifdef CONFIG_WPS - atheros_raw_recv_wps(ctx, src_addr, buf, len); -#endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R - atheros_raw_recv_11r(ctx, src_addr, buf, len); -#endif /* CONFIG_IEEE80211R */ -#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) - atheros_raw_recv_11v(ctx, src_addr, buf, len); -#endif /* CONFIG_WNM */ -#ifdef CONFIG_HS20 - atheros_raw_recv_hs20(ctx, src_addr, buf, len); -#endif /* CONFIG_HS20 */ -} -#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ +#endif static int atheros_receive_pkt(struct atheros_driver_data *drv) { @@ -1334,7 +1305,7 @@ static void fetch_pending_big_events(struct atheros_driver_data *drv) while (1) { os_memset(&iwr, 0, sizeof(iwr)); - os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (void *) tbuf; iwr.u.data.length = sizeof(tbuf); @@ -1529,8 +1500,9 @@ atheros_get_we_version(struct atheros_driver_data *drv) sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); - free(range); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); + os_free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { @@ -1590,8 +1562,9 @@ atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, if (len > sizeof(buf)) { bp = malloc(len); if (bp == NULL) { - printf("EAPOL frame discarded, cannot malloc temp " - "buffer of size %lu!\n", (unsigned long) len); + wpa_printf(MSG_INFO, + "EAPOL frame discarded, cannot malloc temp buffer of size %lu!", + (unsigned long) len); return -1; } } @@ -1628,14 +1601,16 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) drv = os_zalloc(sizeof(struct atheros_driver_data)); if (drv == NULL) { - printf("Could not allocate memory for atheros driver data\n"); + wpa_printf(MSG_INFO, + "Could not allocate memory for atheros driver data"); return NULL; } drv->hapd = hapd; drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); goto bad; } memcpy(drv->iface, params->ifname, sizeof(drv->iface)); @@ -1643,7 +1618,8 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); goto bad; } drv->ifindex = ifr.ifr_ifindex; @@ -1679,8 +1655,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) iwr.u.mode = IW_MODE_MASTER; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { - perror("ioctl[SIOCSIWMODE]"); - printf("Could not set interface to master mode!\n"); + wpa_printf(MSG_ERROR, + "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s", + strerror(errno)); goto bad; } @@ -1746,8 +1723,8 @@ atheros_set_ssid(void *priv, const u8 *buf, int len) iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", + len, strerror(errno)); return -1; } return 0; @@ -1767,7 +1744,8 @@ atheros_get_ssid(void *priv, u8 *buf, int len) IW_ESSID_MAX_SIZE : len; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", + strerror(errno)); ret = -1; } else ret = iwr.u.essid.length; @@ -1832,6 +1810,25 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", params->assocresp_ies); +#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN)) + if (params->osen) { + struct wpa_bss_params bss_params; + + os_memset(&bss_params, 0, sizeof(struct wpa_bss_params)); + bss_params.enabled = 1; + bss_params.wpa = 2; + bss_params.wpa_pairwise = WPA_CIPHER_CCMP; + bss_params.wpa_group = WPA_CIPHER_CCMP; + bss_params.ieee802_1x = 1; + + if (atheros_set_privacy(priv, 1) || + set80211param(priv, IEEE80211_PARAM_OSEN, 1)) + return -1; + + return atheros_set_ieee8021x(priv, &bss_params); + } +#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */ + return 0; } @@ -1963,7 +1960,7 @@ static int atheros_send_action(void *priv, unsigned int freq, } -#ifdef CONFIG_WNM +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, u8 *ie, u16 *len, enum wnm_oper oper) { @@ -2116,7 +2113,7 @@ static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, return -1; } } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ const struct wpa_driver_ops wpa_driver_atheros_ops = { @@ -2150,7 +2147,8 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { .add_sta_node = atheros_add_sta_node, #endif /* CONFIG_IEEE80211R */ .send_action = atheros_send_action, -#ifdef CONFIG_WNM +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) .wnm_oper = atheros_wnm_oper, -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + .set_qos_map = atheros_set_qos_map, }; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 9d869b1517a5..0f1a0f60f27d 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -61,6 +61,9 @@ struct bsd_driver_data { 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 */ @@ -261,17 +264,24 @@ bsd_ctrl_iface(void *priv, int enable) os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { - perror("ioctl[SIOCGIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); return -1; } - if (enable) + if (enable) { + if (ifr.ifr_flags & IFF_UP) + return 0; ifr.ifr_flags |= IFF_UP; - else + } else { + if (!(ifr.ifr_flags & IFF_UP)) + return 0; ifr.ifr_flags &= ~IFF_UP; + } if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { - perror("ioctl[SIOCSIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); return -1; } @@ -284,6 +294,9 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, 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, @@ -338,6 +351,16 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg, } 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 @@ -383,22 +406,24 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) v = IEEE80211_CIPHER_NONE; break; default: - printf("Unknown group key cipher %u\n", - params->wpa_group); + 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)) { - printf("Unable to set group key cipher to %u (%s)\n", - v, ciphernames[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)) { - printf("Unable to set group key length to %u\n", v); + wpa_printf(MSG_INFO, + "Unable to set group key length to %u", v); return -1; } } @@ -412,7 +437,8 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) v |= 1<wpa_key_mgmt); if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS, params->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - params->wpa_key_mgmt); + wpa_printf(MSG_INFO, + "Unable to set key management algorithms to 0x%x", + params->wpa_key_mgmt); return -1; } @@ -431,14 +458,15 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, params->rsn_preauth); if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) { - printf("Unable to set RSN capabilities to 0x%x\n", 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)) { - printf("Unable to set WPA to %u\n", params->wpa); + wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); return -1; } return 0; @@ -473,26 +501,6 @@ bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params) return bsd_ctrl_iface(priv, 1); } -static int -bsd_set_sta_authorized(void *priv, const u8 *addr, - int total_flags, int flags_or, 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); -} - static void bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) { @@ -506,7 +514,8 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) memset(&ie, 0, sizeof(ie)); memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { - printf("Failed to get WPA/RSN information element.\n"); + wpa_printf(MSG_INFO, + "Failed to get WPA/RSN information element"); goto no_ie; } iebuf = ie.wpa_ie; @@ -585,7 +594,7 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) return 0; } -static int +static size_t rtbuf_len(void) { size_t len; @@ -593,7 +602,7 @@ rtbuf_len(void) 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\n", __func__, + wpa_printf(MSG_WARNING, "%s failed: %s", __func__, strerror(errno)); len = 2048; } @@ -651,7 +660,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, wk.ik_keyix = idx; if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) { - printf("Failed to get encryption.\n"); + wpa_printf(MSG_INFO, "Failed to get encryption"); return -1; } @@ -722,37 +731,26 @@ static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = ctx; - char *buf; 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, len; + int n; union wpa_event_data data; - len = rtbuf_len(); - - buf = os_malloc(len); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); - return; - } - - n = read(sock, buf, len); + 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\n", + wpa_printf(MSG_ERROR, "%s read() failed: %s", __func__, strerror(errno)); - os_free(buf); return; } - rtm = (struct rt_msghdr *) buf; + 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); - os_free(buf); return; } ifan = (struct if_announcemsghdr *) rtm; @@ -793,7 +791,6 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) } break; } - os_free(buf); } static void @@ -810,14 +807,23 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) drv = os_zalloc(sizeof(struct bsd_driver_data)); if (drv == NULL) { - printf("Could not allocate memory for bsd driver data\n"); + 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) { - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); goto bad; } os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); @@ -835,7 +841,8 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) drv->route = socket(PF_ROUTE, SOCK_RAW, 0); if (drv->route < 0) { - perror("socket(PF_ROUTE,SOCK_RAW)"); + 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, @@ -853,6 +860,7 @@ bad: l2_packet_deinit(drv->sock_xmit); if (drv->sock >= 0) close(drv->sock); + os_free(drv->event_buf); if (drv != NULL) os_free(drv); return NULL; @@ -873,9 +881,37 @@ bsd_deinit(void *priv) 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, + int total_flags, int flags_or, 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 @@ -1053,9 +1089,9 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) return -1; - privacy = !(params->pairwise_suite == CIPHER_NONE && - params->group_suite == CIPHER_NONE && - params->key_mgmt_suite == KEY_MGMT_NONE && + 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); @@ -1151,7 +1187,6 @@ static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { struct bsd_driver_data *drv = sock_ctx; - char *buf; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; @@ -1159,30 +1194,20 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) struct ieee80211_michael_event *mic; struct ieee80211_leave_event *leave; struct ieee80211_join_event *join; - int n, len; - - len = rtbuf_len(); - - buf = os_malloc(len); - if (buf == NULL) { - wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); - return; - } + int n; - n = read(sock, buf, len); + 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\n", + wpa_printf(MSG_ERROR, "%s read() failed: %s", __func__, strerror(errno)); - os_free(buf); return; } - rtm = (struct rt_msghdr *) buf; + 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); - os_free(buf); return; } os_memset(&event, 0, sizeof(event)); @@ -1197,7 +1222,6 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) case IFAN_DEPARTURE: event.interface_status.ievent = EVENT_INTERFACE_REMOVED; default: - os_free(buf); return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", @@ -1270,7 +1294,6 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) } break; } - os_free(buf); } static void @@ -1295,6 +1318,11 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, 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); @@ -1316,7 +1344,12 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, *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); @@ -1436,6 +1469,33 @@ static int wpa_driver_bsd_capa(struct bsd_driver_data *drv) 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_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) { @@ -1446,6 +1506,15 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) 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 @@ -1461,6 +1530,12 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) 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; @@ -1468,11 +1543,6 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) wpa_driver_bsd_event_receive, ctx, drv); drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - - /* Down interface during setup. */ - if (bsd_ctrl_iface(drv, 0) < 0) - goto fail; if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", @@ -1493,10 +1563,13 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) 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 @@ -1522,6 +1595,7 @@ wpa_driver_bsd_deinit(void *priv) 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); } @@ -1548,6 +1622,8 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .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, @@ -1566,6 +1642,5 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .hapd_set_ssid = bsd_set_ssid, .hapd_get_ssid = bsd_get_ssid, .hapd_send_eapol = bsd_send_eapol, - .sta_set_flags = bsd_set_sta_authorized, .set_generic_elem = bsd_set_opt_ie, }; diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 418cf1a064f7..aebea8cf64e3 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -44,15 +44,12 @@ const char * event_to_string(enum wpa_event_type event) E2S(ASSOC_REJECT); E2S(AUTH_TIMED_OUT); E2S(ASSOC_TIMED_OUT); - E2S(FT_RRB_RX); E2S(WPS_BUTTON_PUSHED); E2S(TX_STATUS); E2S(RX_FROM_UNKNOWN); E2S(RX_MGMT); - E2S(RX_ACTION); E2S(REMAIN_ON_CHANNEL); E2S(CANCEL_REMAIN_ON_CHANNEL); - E2S(MLME_RX); E2S(RX_PROBE_REQ); E2S(NEW_STA); E2S(EAPOL_RX); @@ -65,13 +62,6 @@ const char * event_to_string(enum wpa_event_type event) E2S(UNPROT_DEAUTH); E2S(UNPROT_DISASSOC); E2S(STATION_LOW_ACK); - E2S(P2P_DEV_FOUND); - E2S(P2P_GO_NEG_REQ_RX); - E2S(P2P_GO_NEG_COMPLETED); - E2S(P2P_PROV_DISC_REQUEST); - E2S(P2P_PROV_DISC_RESPONSE); - E2S(P2P_SD_REQUEST); - E2S(P2P_SD_RESPONSE); E2S(IBSS_PEER_LOST); E2S(DRIVER_GTK_REKEY); E2S(SCHED_SCAN_STOPPED); @@ -79,8 +69,152 @@ const char * event_to_string(enum wpa_event_type event) 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/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 16f5563af93b..84b98fb8c87f 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -214,7 +214,7 @@ static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); return; } @@ -229,19 +229,21 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (drv->sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); return -1; } if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { - printf("Could not register read socket\n"); + wpa_printf(MSG_ERROR, "Could not register read socket"); return -1; } memset(&ifr, 0, sizeof(ifr)); snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); return -1; } @@ -256,7 +258,7 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) addr.sll_ifindex); if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } @@ -361,9 +363,9 @@ static int hostap_set_iface_flags(void *priv, int dev_up) os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_mtu = HOSTAPD_MTU; if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { - perror("ioctl[SIOCSIFMTU]"); - printf("Setting MTU failed - trying to survive with " - "current value\n"); + wpa_printf(MSG_INFO, + "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s", + strerror(errno)); } } @@ -383,7 +385,8 @@ static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, iwr.u.data.length = len; if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s", + strerror(errno)); return -1; } @@ -497,7 +500,8 @@ static int hostap_ioctl_prism2param(void *priv, int param, int value) *i++ = value; if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s", + strerror(errno)); return -1; } @@ -554,8 +558,8 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len) iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", + len, strerror(errno)); return -1; } @@ -919,8 +923,9 @@ static int hostap_get_we_version(struct hostap_driver_data *drv) sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); - free(range); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); + os_free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { @@ -975,23 +980,25 @@ static void * hostap_init(struct hostapd_data *hapd, drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); - free(drv); + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + os_free(drv); return NULL; } if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { - printf("Could not enable hostapd mode for interface %s\n", - drv->iface); + wpa_printf(MSG_ERROR, + "Could not enable hostapd mode for interface %s", + drv->iface); close(drv->ioctl_sock); - free(drv); + os_free(drv); return NULL; } if (hostap_init_sockets(drv, params->own_addr) || hostap_wireless_event_init(drv)) { close(drv->ioctl_sock); - free(drv); + os_free(drv); return NULL; } @@ -1060,7 +1067,8 @@ static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) iwr.u.freq.e = 0; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", + strerror(errno)); return -1; } diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c new file mode 100644 index 000000000000..3eae2f89d20e --- /dev/null +++ b/src/drivers/driver_macsec_qca.c @@ -0,0 +1,891 @@ +/* + * Wired Ethernet driver interface for QCA MACsec driver + * Copyright (c) 2005-2009, Jouni Malinen + * Copyright (c) 2004, Gunter Burchardt + * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#include +#include +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ +#ifdef __sun__ +#include +#endif /* __sun__ */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/defs.h" +#include "common/ieee802_1x_defs.h" +#include "driver.h" + +#include "nss_macsec_secy.h" +#include "nss_macsec_secy_rx.h" +#include "nss_macsec_secy_tx.h" + +#define MAXSC 16 + +/* TCI field definition */ +#define TCI_ES 0x40 +#define TCI_SC 0x20 +#define TCI_SCB 0x10 +#define TCI_E 0x08 +#define TCI_C 0x04 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct macsec_qca_data { + char ifname[IFNAMSIZ + 1]; + u32 secy_id; + void *ctx; + + int sock; /* raw packet socket for driver access */ + int pf_sock; + int membership, multi, iff_allmulti, iff_up; + + /* shadow */ + Boolean always_include_sci; + Boolean use_es; + Boolean use_scb; + Boolean protect_frames; + Boolean replay_protect; + u32 replay_window; +}; + + +static int macsec_qca_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__ */ +} + + +static int macsec_qca_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int macsec_qca_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for macsec connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int macsec_qca_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 macsec_qca_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 macsec_qca_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 macsec_qca_get_ifstatus(const char *ifname, int *status) +{ + struct ifmediareq ifmr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_print(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 macsec_qca_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 __macsec_drv_init(struct macsec_qca_data *drv) +{ + int ret = 0; + fal_rx_ctl_filt_t rx_ctl_filt; + fal_tx_ctl_filt_t tx_ctl_filt; + + wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id); + + /* Enable Secy and Let EAPoL bypass */ + ret = nss_macsec_secy_en_set(drv->secy_id, TRUE); + if (ret) + wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL"); + + ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id, + FAL_SC_SA_MAP_1_4); + if (ret) + wpa_printf(MSG_ERROR, + "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL"); + + os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt)); + rx_ctl_filt.bypass = 1; + rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE; + rx_ctl_filt.match_mask = 0xffff; + rx_ctl_filt.ether_type_da_range = 0x888e; + ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt); + if (ret) + wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL"); + + os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt)); + tx_ctl_filt.bypass = 1; + tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE; + tx_ctl_filt.match_mask = 0xffff; + tx_ctl_filt.ether_type_da_range = 0x888e; + ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt); + if (ret) + wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL"); +} + + +static void __macsec_drv_deinit(struct macsec_qca_data *drv) +{ + nss_macsec_secy_en_set(drv->secy_id, FALSE); + nss_macsec_secy_rx_sc_del_all(drv->secy_id); + nss_macsec_secy_tx_sc_del_all(drv->secy_id); +} + + +static void * macsec_qca_init(void *ctx, const char *ifname) +{ + struct macsec_qca_data *drv; + int flags; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ctx = ctx; + + /* Board specific settings */ + if (os_memcmp("eth2", drv->ifname, 4) == 0) + drv->secy_id = 1; + else if (os_memcmp("eth3", drv->ifname, 4) == 0) + drv->secy_id = 2; + else + drv->secy_id = -1; + +#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 (macsec_qca_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (macsec_qca_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 (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, + "%s: Added multicast membership with SIOCADDMULTI", + __func__); + drv->multi = 1; + } else if (macsec_qca_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 (macsec_qca_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 (macsec_qca_get_ifstatus(ifname, &status) == 0 && + status == 0) + sleep(1); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + return drv; +} + + +static void macsec_qca_deinit(void *priv) +{ + struct macsec_qca_data *drv = priv; + int flags; + + if (drv->membership && + macsec_qca_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 && + macsec_qca_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 && + (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 || + macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (drv->iff_up && + macsec_qca_get_ifflags(drv->ifname, &flags) == 0 && + (flags & IFF_UP) && + macsec_qca_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); +} + + +static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params) +{ + struct macsec_qca_data *drv = priv; + + drv->always_include_sci = params->always_include_sci; + drv->use_es = params->use_es; + drv->use_scb = params->use_scb; + + wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d", + __func__, drv->use_es, drv->use_scb, + drv->always_include_sci); + + __macsec_drv_init(drv); + + return 0; +} + + +static int macsec_qca_macsec_deinit(void *priv) +{ + struct macsec_qca_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + __macsec_drv_deinit(drv); + + return 0; +} + + +static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + drv->protect_frames = enabled; + + return ret; +} + + +static int macsec_qca_set_replay_protect(void *priv, Boolean enabled, + unsigned int window) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u", + __func__, enabled, window); + + drv->replay_protect = enabled; + drv->replay_window = window; + + return ret; +} + + +static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs, + size_t cs_len) +{ + u8 default_cs_id[] = CS_ID_GCM_AES_128; + + if (cs_len != CS_ID_LEN || + os_memcmp(cs, default_cs_id, cs_len) != 0) { + wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite", + cs, cs_len); + return -1; + } + + /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */ + wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__); + + return 0; +} + + +static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled); + + ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled); + + return ret; +} + + +static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an, + u32 *lowest_pn) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + u32 next_pn = 0; + bool enabled = FALSE; + u32 win; + + ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an, + &next_pn); + ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel, + &enabled); + ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id, + channel, &win); + + if (enabled) + *lowest_pn = (next_pn > win) ? (next_pn - win) : 1; + else + *lowest_pn = next_pn; + + wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn); + + return ret; +} + + +static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an, + u32 *next_pn) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an, + next_pn); + + wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn); + + return ret; +} + + +int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an, + next_pn); + + wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn); + + return ret; +} + + +static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + u32 sc_ch = 0; + bool in_use = FALSE; + + for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) { + ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch, + &in_use); + if (ret) + continue; + + if (!in_use) { + *channel = sc_ch; + wpa_printf(MSG_DEBUG, "%s: channel=%d", + __func__, *channel); + return 0; + } + } + + wpa_printf(MSG_DEBUG, "%s: no available channel", __func__); + + return -1; +} + + +static int macsec_qca_create_receive_sc(void *priv, u32 channel, + const u8 *sci_addr, u16 sci_port, + unsigned int conf_offset, + int validation) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + fal_rx_prc_lut_t entry; + fal_rx_sc_validate_frame_e vf; + enum validate_frames validate_frames = validation; + + wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); + + /* rx prc lut */ + os_memset(&entry, 0, sizeof(entry)); + + os_memcpy(entry.sci, sci_addr, ETH_ALEN); + entry.sci[6] = (sci_port >> 8) & 0xf; + entry.sci[7] = sci_port & 0xf; + entry.sci_mask = 0xf; + + entry.valid = 1; + entry.channel = channel; + entry.action = FAL_RX_PRC_ACTION_PROCESS; + entry.offset = conf_offset; + + /* rx validate frame */ + if (validate_frames == Strict) + vf = FAL_RX_SC_VALIDATE_FRAME_STRICT; + else if (validate_frames == Checked) + vf = FAL_RX_SC_VALIDATE_FRAME_CHECK; + else + vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED; + + ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry); + ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel); + ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel, + vf); + ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel, + drv->replay_protect); + ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id, + channel, + drv->replay_window); + + return ret; +} + + +static int macsec_qca_delete_receive_sc(void *priv, u32 channel) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + fal_rx_prc_lut_t entry; + + wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); + + /* rx prc lut */ + os_memset(&entry, 0, sizeof(entry)); + + ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel); + ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry); + + return ret; +} + + +static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an, + u32 lowest_pn, const u8 *sak) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + fal_rx_sak_t rx_sak; + int i = 0; + + wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x", + __func__, channel, an, lowest_pn); + + os_memset(&rx_sak, 0, sizeof(rx_sak)); + for (i = 0; i < 16; i++) + rx_sak.sak[i] = sak[15 - i]; + + ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an); + ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak); + + return ret; +} + + +static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + + ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE); + + return ret; +} + + +static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + + ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE); + + return ret; +} + + +static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + u32 sc_ch = 0; + bool in_use = FALSE; + + for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) { + ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch, + &in_use); + if (ret) + continue; + + if (!in_use) { + *channel = sc_ch; + wpa_printf(MSG_DEBUG, "%s: channel=%d", + __func__, *channel); + return 0; + } + } + + wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__); + + return -1; +} + + +static int macsec_qca_create_transmit_sc(void *priv, u32 channel, + const u8 *sci_addr, u16 sci_port, + unsigned int conf_offset) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + fal_tx_class_lut_t entry; + u8 psci[ETH_ALEN + 2]; + + wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); + + /* class lut */ + os_memset(&entry, 0, sizeof(entry)); + + entry.valid = 1; + entry.action = FAL_TX_CLASS_ACTION_FORWARD; + entry.channel = channel; + + os_memcpy(psci, sci_addr, ETH_ALEN); + psci[6] = (sci_port >> 8) & 0xf; + psci[7] = sci_port & 0xf; + + ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry); + ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8); + ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel, + drv->protect_frames); + ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id, + channel, + conf_offset); + + return ret; +} + + +static int macsec_qca_delete_transmit_sc(void *priv, u32 channel) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + fal_tx_class_lut_t entry; + + wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); + + /* class lut */ + os_memset(&entry, 0, sizeof(entry)); + + ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry); + ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel); + + return ret; +} + + +static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an, + u32 next_pn, Boolean confidentiality, + const u8 *sak) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + u8 tci = 0; + fal_tx_sak_t tx_sak; + int i; + + wpa_printf(MSG_DEBUG, + "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d", + __func__, channel, an, next_pn, confidentiality); + + if (drv->always_include_sci) + tci |= TCI_SC; + else if (drv->use_es) + tci |= TCI_ES; + else if (drv->use_scb) + tci |= TCI_SCB; + + if (confidentiality) + tci |= TCI_E | TCI_C; + + os_memset(&tx_sak, 0, sizeof(tx_sak)); + for (i = 0; i < 16; i++) + tx_sak.sak[i] = sak[15 - i]; + + ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an, + next_pn); + ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak); + ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel, + (tci >> 2)); + ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an); + + return ret; +} + + +static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + + ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE); + + return ret; +} + + +static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an) +{ + struct macsec_qca_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + + ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE); + + return ret; +} + + +const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { + .name = "macsec_qca", + .desc = "QCA MACsec Ethernet driver", + .get_ssid = macsec_qca_get_ssid, + .get_bssid = macsec_qca_get_bssid, + .get_capa = macsec_qca_get_capa, + .init = macsec_qca_init, + .deinit = macsec_qca_deinit, + + .macsec_init = macsec_qca_macsec_init, + .macsec_deinit = macsec_qca_macsec_deinit, + .enable_protect_frames = macsec_qca_enable_protect_frames, + .set_replay_protect = macsec_qca_set_replay_protect, + .set_current_cipher_suite = macsec_qca_set_current_cipher_suite, + .enable_controlled_port = macsec_qca_enable_controlled_port, + .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn, + .get_transmit_next_pn = macsec_qca_get_transmit_next_pn, + .set_transmit_next_pn = macsec_qca_set_transmit_next_pn, + .get_available_receive_sc = macsec_qca_get_available_receive_sc, + .create_receive_sc = macsec_qca_create_receive_sc, + .delete_receive_sc = macsec_qca_delete_receive_sc, + .create_receive_sa = macsec_qca_create_receive_sa, + .enable_receive_sa = macsec_qca_enable_receive_sa, + .disable_receive_sa = macsec_qca_disable_receive_sa, + .get_available_transmit_sc = macsec_qca_get_available_transmit_sc, + .create_transmit_sc = macsec_qca_create_transmit_sc, + .delete_transmit_sc = macsec_qca_delete_transmit_sc, + .create_transmit_sa = macsec_qca_create_transmit_sa, + .enable_transmit_sa = macsec_qca_enable_transmit_sa, + .disable_transmit_sa = macsec_qca_disable_transmit_sa, +}; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c deleted file mode 100644 index bb48011dfe57..000000000000 --- a/src/drivers/driver_madwifi.c +++ /dev/null @@ -1,1313 +0,0 @@ -/* - * WPA Supplicant - driver interaction with MADWIFI 802.11 driver - * Copyright (c) 2004, Sam Leffler - * Copyright (c) 2004, Video54 Technologies - * Copyright (c) 2004-2007, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - * - * While this driver wrapper supports both AP (hostapd) and station - * (wpa_supplicant) operations, the station side is deprecated and - * driver_wext.c should be used instead. This driver wrapper should only be - * used with hostapd for AP mode functionality. - */ - -#include "includes.h" -#include - -#include "common.h" -#include "driver.h" -#include "driver_wext.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "linux_wext.h" - -/* - * Avoid conflicts with wpa_supplicant definitions by undefining a definition. - */ -#undef WME_OUI_TYPE - -#include -#include -#ifdef WME_NUM_AC -/* Assume this is built against BSD branch of madwifi driver. */ -#define MADWIFI_BSD -#include -#endif /* WME_NUM_AC */ -#include -#include - -#ifdef CONFIG_WPS -#ifdef IEEE80211_IOCTL_FILTERFRAME -#include - -#ifndef ETH_P_80211_RAW -#define ETH_P_80211_RAW 0x0019 -#endif -#endif /* IEEE80211_IOCTL_FILTERFRAME */ -#endif /* CONFIG_WPS */ - -/* - * Avoid conflicts with hostapd definitions by undefining couple of defines - * from madwifi header files. - */ -#undef RSN_VERSION -#undef WPA_VERSION -#undef WPA_OUI_TYPE -#undef WME_OUI_TYPE - - -#ifdef IEEE80211_IOCTL_SETWMMPARAMS -/* Assume this is built against madwifi-ng */ -#define MADWIFI_NG -#endif /* IEEE80211_IOCTL_SETWMMPARAMS */ - -#define WPA_KEY_RSC_LEN 8 - -#include "priv_netlink.h" -#include "netlink.h" -#include "linux_ioctl.h" -#include "l2_packet/l2_packet.h" - - -struct madwifi_driver_data { - struct hostapd_data *hapd; /* back pointer */ - - char iface[IFNAMSIZ + 1]; - int ifindex; - struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ - struct l2_packet_data *sock_recv; /* raw packet recv socket */ - int ioctl_sock; /* socket for ioctl() use */ - struct netlink_data *netlink; - int we_version; - u8 acct_mac[ETH_ALEN]; - struct hostap_sta_driver_data acct_data; - - struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ -}; - -static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - int reason_code); - -static int -set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) -{ - struct iwreq iwr; - int do_inline = len < IFNAMSIZ; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); -#ifdef IEEE80211_IOCTL_FILTERFRAME - /* FILTERFRAME must be NOT inline, regardless of size. */ - if (op == IEEE80211_IOCTL_FILTERFRAME) - do_inline = 0; -#endif /* IEEE80211_IOCTL_FILTERFRAME */ - if (op == IEEE80211_IOCTL_SET_APPIEBUF) - do_inline = 0; - if (do_inline) { - /* - * Argument data fits inline; put it there. - */ - memcpy(iwr.u.name, data, len); - } else { - /* - * Argument data too big for inline transfer; setup a - * parameter block instead; the kernel will transfer - * the data for the driver. - */ - iwr.u.data.pointer = data; - iwr.u.data.length = len; - } - - if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { -#ifdef MADWIFI_NG - int first = IEEE80211_IOCTL_SETPARAM; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETMODE]", - "ioctl[IEEE80211_IOCTL_GETMODE]", - "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_SETCHANLIST]", - "ioctl[IEEE80211_IOCTL_GETCHANLIST]", - "ioctl[IEEE80211_IOCTL_CHANSWITCH]", - "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", - "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", - "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", - "ioctl[IEEE80211_IOCTL_FILTERFRAME]", - "ioctl[IEEE80211_IOCTL_GETCHANINFO]", - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_SETMLME]", - NULL, - "ioctl[IEEE80211_IOCTL_SETKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_DELKEY]", - NULL, - "ioctl[IEEE80211_IOCTL_ADDMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_DELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_WDSMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_WDSDELMAC]", - NULL, - "ioctl[IEEE80211_IOCTL_KICKMAC]", - }; -#else /* MADWIFI_NG */ - int first = IEEE80211_IOCTL_SETPARAM; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETKEY]", - "ioctl[SIOCIWFIRSTPRIV+3]", - "ioctl[IEEE80211_IOCTL_DELKEY]", - "ioctl[SIOCIWFIRSTPRIV+5]", - "ioctl[IEEE80211_IOCTL_SETMLME]", - "ioctl[SIOCIWFIRSTPRIV+7]", - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_ADDMAC]", - "ioctl[SIOCIWFIRSTPRIV+11]", - "ioctl[IEEE80211_IOCTL_DELMAC]", - "ioctl[SIOCIWFIRSTPRIV+13]", - "ioctl[IEEE80211_IOCTL_CHANLIST]", - "ioctl[SIOCIWFIRSTPRIV+15]", - "ioctl[IEEE80211_IOCTL_GETRSN]", - "ioctl[SIOCIWFIRSTPRIV+17]", - "ioctl[IEEE80211_IOCTL_GETKEY]", - }; -#endif /* MADWIFI_NG */ - int idx = op - first; - if (first <= op && - idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && - opnames[idx]) - perror(opnames[idx]); - else - perror("ioctl[unknown???]"); - return -1; - } - return 0; -} - -static int -set80211param(struct madwifi_driver_data *drv, int op, int arg) -{ - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.mode = op; - memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); - - if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { - perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); - wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " - "arg %d)", __func__, op, arg); - return -1; - } - return 0; -} - -#ifndef CONFIG_NO_STDOUT_DEBUG -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; -} -#endif /* CONFIG_NO_STDOUT_DEBUG */ - -/* - * Configure WPA parameters. - */ -static int -madwifi_configure_wpa(struct madwifi_driver_data *drv, - struct wpa_bss_params *params) -{ - 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_ERROR, "Unknown group key cipher %u", - params->wpa_group); - return -1; - } - wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); - if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { - printf("Unable to set group key cipher to %u\n", 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(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { - printf("Unable to set group key length to %u\n", v); - return -1; - } - } - - v = 0; - if (params->wpa_pairwise & WPA_CIPHER_CCMP) - v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<wpa_key_mgmt); - if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, - params->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - 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(drv, IEEE80211_PARAM_RSNCAPS, v)) { - printf("Unable to set RSN capabilities to 0x%x\n", v); - return -1; - } - - wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); - if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { - printf("Unable to set WPA to %u\n", params->wpa); - return -1; - } - return 0; -} - -static int -madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) -{ - struct madwifi_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); - - if (!params->enabled) { - /* XXX restore state */ - return set80211param(priv, IEEE80211_PARAM_AUTHMODE, - IEEE80211_AUTH_AUTO); - } - if (!params->wpa && !params->ieee802_1x) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); - return -1; - } - if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); - return -1; - } - if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, - (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); - return -1; - } - - return 0; -} - -static int -madwifi_set_privacy(void *priv, int enabled) -{ - struct madwifi_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - - return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); -} - -static int -madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", - __func__, ether_sprintf(addr), authorized); - - if (authorized) - mlme.im_op = IEEE80211_MLME_AUTHORIZE; - else - mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; - mlme.im_reason = 0; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, - __func__, authorized ? "" : "un", MAC2STR(addr)); - } - - return ret; -} - -static int -madwifi_sta_set_flags(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) -{ - /* For now, only support setting Authorized flag */ - if (flags_or & WPA_STA_AUTHORIZED) - return madwifi_set_sta_authorized(priv, addr, 1); - if (!(flags_and & WPA_STA_AUTHORIZED)) - return madwifi_set_sta_authorized(priv, addr, 0); - return 0; -} - -static int -madwifi_del_key(void *priv, const u8 *addr, int key_idx) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_del_key wk; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", - __func__, ether_sprintf(addr), key_idx); - - memset(&wk, 0, sizeof(wk)); - if (addr != NULL) { - memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); - wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; - } else { - wk.idk_keyix = key_idx; - } - - ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" - " key_idx %d)", __func__, ether_sprintf(addr), - key_idx); - } - - return ret; -} - -static int -wpa_driver_madwifi_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 madwifi_driver_data *drv = priv; - struct ieee80211req_key wk; - u_int8_t cipher; - int ret; - - if (alg == WPA_ALG_NONE) - return madwifi_del_key(drv, addr, key_idx); - - wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", - __func__, alg, ether_sprintf(addr), key_idx); - - if (alg == WPA_ALG_WEP) - cipher = IEEE80211_CIPHER_WEP; - else if (alg == WPA_ALG_TKIP) - cipher = IEEE80211_CIPHER_TKIP; - else if (alg == WPA_ALG_CCMP) - cipher = IEEE80211_CIPHER_AES_CCM; - else { - printf("%s: unknown/unsupported algorithm %d\n", - __func__, alg); - return -1; - } - - if (key_len > sizeof(wk.ik_keydata)) { - printf("%s: key length %lu too big\n", __func__, - (unsigned long) key_len); - return -3; - } - - memset(&wk, 0, sizeof(wk)); - wk.ik_type = cipher; - wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; - if (addr == NULL || is_broadcast_ether_addr(addr)) { - memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); - wk.ik_keyix = key_idx; - wk.ik_flags |= IEEE80211_KEY_DEFAULT; - } else { - memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); - wk.ik_keyix = IEEE80211_KEYIX_NONE; - } - wk.ik_keylen = key_len; - memcpy(wk.ik_keydata, key, key_len); - - ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" - " key_idx %d alg %d key_len %lu set_tx %d)", - __func__, ether_sprintf(wk.ik_macaddr), key_idx, - alg, (unsigned long) key_len, set_tx); - } - - return ret; -} - - -static int -madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, - u8 *seq) -{ - struct madwifi_driver_data *drv = priv; - 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 (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { - wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " - "(addr " MACSTR " key_idx %d)", - __func__, MAC2STR(wk.ik_macaddr), idx); - 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 -madwifi_flush(void *priv) -{ -#ifdef MADWIFI_BSD - u8 allsta[IEEE80211_ADDR_LEN]; - memset(allsta, 0xff, IEEE80211_ADDR_LEN); - return madwifi_sta_deauth(priv, NULL, allsta, - IEEE80211_REASON_AUTH_LEAVE); -#else /* MADWIFI_BSD */ - return 0; /* XXX */ -#endif /* MADWIFI_BSD */ -} - - -static int -madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, - const u8 *addr) -{ - struct madwifi_driver_data *drv = priv; - -#ifdef MADWIFI_BSD - struct ieee80211req_sta_stats stats; - - memset(data, 0, sizeof(*data)); - - /* - * Fetch statistics for station from the system. - */ - memset(&stats, 0, sizeof(stats)); - memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); - if (set80211priv(drv, -#ifdef MADWIFI_NG - IEEE80211_IOCTL_STA_STATS, -#else /* MADWIFI_NG */ - IEEE80211_IOCTL_GETSTASTATS, -#endif /* MADWIFI_NG */ - &stats, sizeof(stats))) { - wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " - MACSTR ")", __func__, MAC2STR(addr)); - if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { - memcpy(data, &drv->acct_data, sizeof(*data)); - return 0; - } - - printf("Failed to get station stats information element.\n"); - return -1; - } - - 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; - -#else /* MADWIFI_BSD */ - - char buf[1024], line[128], *pos; - FILE *f; - unsigned long val; - - memset(data, 0, sizeof(*data)); - snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, - drv->iface, MAC2STR(addr)); - - f = fopen(buf, "r"); - if (!f) { - if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) - return -1; - memcpy(data, &drv->acct_data, sizeof(*data)); - return 0; - } - /* Need to read proc file with in one piece, so use large enough - * buffer. */ - setbuffer(f, buf, sizeof(buf)); - - while (fgets(line, sizeof(line), f)) { - pos = strchr(line, '='); - if (!pos) - continue; - *pos++ = '\0'; - val = strtoul(pos, NULL, 10); - if (strcmp(line, "rx_packets") == 0) - data->rx_packets = val; - else if (strcmp(line, "tx_packets") == 0) - data->tx_packets = val; - else if (strcmp(line, "rx_bytes") == 0) - data->rx_bytes = val; - else if (strcmp(line, "tx_bytes") == 0) - data->tx_bytes = val; - } - - fclose(f); - - return 0; -#endif /* MADWIFI_BSD */ -} - - -static int -madwifi_sta_clear_stats(void *priv, const u8 *addr) -{ -#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS) - struct madwifi_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); - - mlme.im_op = IEEE80211_MLME_CLEAR_STATS; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, - sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " - MACSTR ")", __func__, MAC2STR(addr)); - } - - return ret; -#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ - return 0; /* FIX */ -#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ -} - - -static int -madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) -{ - /* - * Do nothing; we setup parameters at startup that define the - * contents of the beacon information element. - */ - return 0; -} - -static int -madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - int reason_code) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", - __func__, ether_sprintf(addr), reason_code); - - mlme.im_op = IEEE80211_MLME_DEAUTH; - mlme.im_reason = reason_code; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR - " reason %d)", - __func__, MAC2STR(addr), reason_code); - } - - return ret; -} - -static int -madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - int reason_code) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - int ret; - - wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", - __func__, ether_sprintf(addr), reason_code); - - mlme.im_op = IEEE80211_MLME_DISASSOC; - mlme.im_reason = reason_code; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " - MACSTR " reason %d)", - __func__, MAC2STR(addr), reason_code); - } - - return ret; -} - -#ifdef CONFIG_WPS -#ifdef IEEE80211_IOCTL_FILTERFRAME -static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct madwifi_driver_data *drv = ctx; - const struct ieee80211_mgmt *mgmt; - u16 fc; - union wpa_event_data event; - - /* Send Probe Request information to WPS processing */ - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) - return; - mgmt = (const struct ieee80211_mgmt *) buf; - - fc = le_to_host16(mgmt->frame_control); - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || - WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) - return; - - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); -} -#endif /* IEEE80211_IOCTL_FILTERFRAME */ -#endif /* CONFIG_WPS */ - -static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) -{ - int ret = 0; -#ifdef CONFIG_WPS -#ifdef IEEE80211_IOCTL_FILTERFRAME - struct ieee80211req_set_filter filt; - - wpa_printf(MSG_DEBUG, "%s Enter", __func__); - filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; - - ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, - sizeof(struct ieee80211req_set_filter)); - if (ret) - return ret; - - drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, - madwifi_raw_receive, drv, 1); - if (drv->sock_raw == NULL) - return -1; -#endif /* IEEE80211_IOCTL_FILTERFRAME */ -#endif /* CONFIG_WPS */ - return ret; -} - -#ifdef CONFIG_WPS -static int -madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) -{ - struct madwifi_driver_data *drv = priv; - u8 buf[256]; - struct ieee80211req_getset_appiebuf *beac_ie; - - wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, - (unsigned long) len); - - beac_ie = (struct ieee80211req_getset_appiebuf *) buf; - beac_ie->app_frmtype = frametype; - beac_ie->app_buflen = len; - memcpy(&(beac_ie->app_buf[0]), ie, len); - - return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, - sizeof(struct ieee80211req_getset_appiebuf) + len); -} - -static int -madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) -{ - if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, - beacon ? wpabuf_len(beacon) : 0, - IEEE80211_APPIE_FRAME_BEACON) < 0) - return -1; - return madwifi_set_wps_ie(priv, - proberesp ? wpabuf_head(proberesp) : NULL, - proberesp ? wpabuf_len(proberesp) : 0, - IEEE80211_APPIE_FRAME_PROBE_RESP); -} -#else /* CONFIG_WPS */ -#define madwifi_set_ap_wps_ie NULL -#endif /* CONFIG_WPS */ - -static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - struct madwifi_driver_data *drv = priv; - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.freq.m = freq->channel; - iwr.u.freq.e = 0; - - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); - return -1; - } - - return 0; -} - -static void -madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct ieee80211req_wpaie ie; - int ielen = 0; - u8 *iebuf = NULL; - - /* - * Fetch negotiated WPA/RSN parameters from the system. - */ - memset(&ie, 0, sizeof(ie)); - memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); - if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { - wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE", - __func__); - goto no_ie; - } - wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", - ie.wpa_ie, IEEE80211_MAX_OPT_IE); - iebuf = ie.wpa_ie; - /* madwifi seems to return some random data if WPA/RSN IE is not set. - * Assume the IE was not included if the IE type is unknown. */ - if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) - iebuf[1] = 0; -#ifdef MADWIFI_NG - wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", - ie.rsn_ie, IEEE80211_MAX_OPT_IE); - if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { - /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not - * set. This is needed for WPA2. */ - iebuf = ie.rsn_ie; - if (iebuf[0] != WLAN_EID_RSN) - iebuf[1] = 0; - } -#endif /* MADWIFI_NG */ - - ielen = iebuf[1]; - if (ielen == 0) - iebuf = NULL; - else - ielen += 2; - -no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen, 0); - - if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { - /* Cached accounting data is not valid anymore. */ - memset(drv->acct_mac, 0, ETH_ALEN); - memset(&drv->acct_data, 0, sizeof(drv->acct_data)); - } -} - -static void -madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, - char *custom) -{ - wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); - - if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { - char *pos; - u8 addr[ETH_ALEN]; - pos = strstr(custom, "addr="); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "without sender address ignored"); - return; - } - pos += 5; - if (hwaddr_aton(pos, addr) == 0) { - union wpa_event_data data; - os_memset(&data, 0, sizeof(data)); - data.michael_mic_failure.unicast = 1; - data.michael_mic_failure.src = addr; - wpa_supplicant_event(drv->hapd, - EVENT_MICHAEL_MIC_FAILURE, &data); - } else { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "with invalid MAC address"); - } - } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { - char *key, *value; - u32 val; - key = custom; - while ((key = strchr(key, '\n')) != NULL) { - key++; - value = strchr(key, '='); - if (value == NULL) - continue; - *value++ = '\0'; - val = strtoul(value, NULL, 10); - if (strcmp(key, "mac") == 0) - hwaddr_aton(value, drv->acct_mac); - else if (strcmp(key, "rx_packets") == 0) - drv->acct_data.rx_packets = val; - else if (strcmp(key, "tx_packets") == 0) - drv->acct_data.tx_packets = val; - else if (strcmp(key, "rx_bytes") == 0) - drv->acct_data.rx_bytes = val; - else if (strcmp(key, "tx_bytes") == 0) - drv->acct_data.tx_bytes = val; - key = value; - } - } -} - -static void -madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, - char *data, int len) -{ - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf; - - pos = data; - end = data + len; - - while (pos + IW_EV_LCP_LEN <= end) { - /* Event data may be unaligned, so make a local, aligned copy - * before processing. */ - memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", - iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) - return; - - custom = pos + IW_EV_POINT_LEN; - if (drv->we_version > 18 && - (iwe->cmd == IWEVMICHAELMICFAILURE || - iwe->cmd == IWEVCUSTOM)) { - /* WE-19 removed the pointer from struct iw_point */ - char *dpos = (char *) &iwe_buf.u.data.length; - int dlen = dpos - (char *) &iwe_buf; - memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); - } else { - memcpy(&iwe_buf, pos, sizeof(struct iw_event)); - custom += IW_EV_POINT_OFF; - } - - switch (iwe->cmd) { - case IWEVEXPIRED: - drv_event_disassoc(drv->hapd, - (u8 *) iwe->u.addr.sa_data); - break; - case IWEVREGISTERED: - madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); - break; - case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) - return; - buf = malloc(iwe->u.data.length + 1); - if (buf == NULL) - return; /* XXX */ - memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - madwifi_wireless_event_wireless_custom(drv, buf); - free(buf); - break; - } - - pos += iwe->len; - } -} - - -static void -madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, - u8 *buf, size_t len) -{ - struct madwifi_driver_data *drv = ctx; - int attrlen, rta_len; - struct rtattr *attr; - - if (ifi->ifi_index != drv->ifindex) - return; - - attrlen = len; - attr = (struct rtattr *) buf; - - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_WIRELESS) { - madwifi_wireless_event_wireless( - drv, ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } - attr = RTA_NEXT(attr, attrlen); - } -} - - -static int -madwifi_get_we_version(struct madwifi_driver_data *drv) -{ - struct iw_range *range; - struct iwreq iwr; - int minlen; - size_t buflen; - - drv->we_version = 0; - - /* - * Use larger buffer than struct iw_range in order to allow the - * structure to grow in the future. - */ - buflen = sizeof(struct iw_range) + 500; - range = os_zalloc(buflen); - if (range == NULL) - return -1; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.data.pointer = (caddr_t) range; - iwr.u.data.length = buflen; - - minlen = ((char *) &range->enc_capa) - (char *) range + - sizeof(range->enc_capa); - - if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); - free(range); - return -1; - } else if (iwr.u.data.length >= minlen && - range->we_version_compiled >= 18) { - wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " - "WE(source)=%d enc_capa=0x%x", - range->we_version_compiled, - range->we_version_source, - range->enc_capa); - drv->we_version = range->we_version_compiled; - } - - free(range); - return 0; -} - - -static int -madwifi_wireless_event_init(struct madwifi_driver_data *drv) -{ - struct netlink_config *cfg; - - madwifi_get_we_version(drv); - - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - return -1; - cfg->ctx = drv; - cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); - return -1; - } - - return 0; -} - - -static int -madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr, u32 flags) -{ - struct madwifi_driver_data *drv = priv; - unsigned char buf[3000]; - unsigned char *bp = buf; - struct l2_ethhdr *eth; - size_t len; - int status; - - /* - * Prepend the Ethernet header. If the caller left us - * space at the front we could just insert it but since - * we don't know we copy to a local buffer. Given the frequency - * and size of frames this probably doesn't matter. - */ - len = data_len + sizeof(struct l2_ethhdr); - if (len > sizeof(buf)) { - bp = malloc(len); - if (bp == NULL) { - printf("EAPOL frame discarded, cannot malloc temp " - "buffer of size %lu!\n", (unsigned long) len); - return -1; - } - } - eth = (struct l2_ethhdr *) bp; - memcpy(eth->h_dest, addr, ETH_ALEN); - memcpy(eth->h_source, own_addr, ETH_ALEN); - eth->h_proto = host_to_be16(ETH_P_EAPOL); - memcpy(eth+1, data, data_len); - - wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); - - status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); - - if (bp != buf) - free(bp); - return status; -} - -static void -handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) -{ - struct madwifi_driver_data *drv = ctx; - drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), - len - sizeof(struct l2_ethhdr)); -} - -static void * -madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) -{ - struct madwifi_driver_data *drv; - struct ifreq ifr; - struct iwreq iwr; - char brname[IFNAMSIZ]; - - drv = os_zalloc(sizeof(struct madwifi_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for madwifi driver data\n"); - return NULL; - } - - drv->hapd = hapd; - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); - goto bad; - } - memcpy(drv->iface, params->ifname, sizeof(drv->iface)); - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); - if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); - goto bad; - } - drv->ifindex = ifr.ifr_ifindex; - - drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, - handle_read, drv, 1); - if (drv->sock_xmit == NULL) - goto bad; - if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) - goto bad; - if (params->bridge[0]) { - wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", - params->bridge[0]); - drv->sock_recv = l2_packet_init(params->bridge[0], NULL, - ETH_P_EAPOL, handle_read, drv, - 1); - if (drv->sock_recv == NULL) - goto bad; - } else if (linux_br_get(brname, drv->iface) == 0) { - wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " - "EAPOL receive", brname); - drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, - handle_read, drv, 1); - if (drv->sock_recv == NULL) - goto bad; - } else - drv->sock_recv = drv->sock_xmit; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - - iwr.u.mode = IW_MODE_MASTER; - - if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { - perror("ioctl[SIOCSIWMODE]"); - printf("Could not set interface to master mode!\n"); - goto bad; - } - - /* mark down during setup */ - linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - madwifi_set_privacy(drv, 0); /* default to no privacy */ - - madwifi_receive_probe_req(drv); - - if (madwifi_wireless_event_init(drv)) - goto bad; - - return drv; -bad: - if (drv->sock_xmit != NULL) - l2_packet_deinit(drv->sock_xmit); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - if (drv != NULL) - free(drv); - return NULL; -} - - -static void -madwifi_deinit(void *priv) -{ - struct madwifi_driver_data *drv = priv; - - netlink_deinit(drv->netlink); - (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) - l2_packet_deinit(drv->sock_recv); - if (drv->sock_xmit != NULL) - l2_packet_deinit(drv->sock_xmit); - if (drv->sock_raw) - l2_packet_deinit(drv->sock_raw); - free(drv); -} - -static int -madwifi_set_ssid(void *priv, const u8 *buf, int len) -{ - struct madwifi_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.essid.flags = 1; /* SSID active */ - iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len + 1; - - if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); - return -1; - } - return 0; -} - -static int -madwifi_get_ssid(void *priv, u8 *buf, int len) -{ - struct madwifi_driver_data *drv = priv; - struct iwreq iwr; - int ret = 0; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len; - - if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); - ret = -1; - } else - ret = iwr.u.essid.length; - - return ret; -} - -static int -madwifi_set_countermeasures(void *priv, int enabled) -{ - struct madwifi_driver_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); -} - -static int -madwifi_commit(void *priv) -{ - struct madwifi_driver_data *drv = priv; - return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); -} - - -const struct wpa_driver_ops wpa_driver_madwifi_ops = { - .name = "madwifi", - .desc = "MADWIFI 802.11 support (Atheros, etc.)", - .set_key = wpa_driver_madwifi_set_key, - .hapd_init = madwifi_init, - .hapd_deinit = madwifi_deinit, - .set_ieee8021x = madwifi_set_ieee8021x, - .set_privacy = madwifi_set_privacy, - .get_seqnum = madwifi_get_seqnum, - .flush = madwifi_flush, - .set_generic_elem = madwifi_set_opt_ie, - .sta_set_flags = madwifi_sta_set_flags, - .read_sta_data = madwifi_read_sta_driver_data, - .hapd_send_eapol = madwifi_send_eapol, - .sta_disassoc = madwifi_sta_disassoc, - .sta_deauth = madwifi_sta_deauth, - .hapd_set_ssid = madwifi_set_ssid, - .hapd_get_ssid = madwifi_get_ssid, - .hapd_set_countermeasures = madwifi_set_countermeasures, - .sta_clear_stats = madwifi_sta_clear_stats, - .commit = madwifi_commit, - .set_ap_wps_ie = madwifi_set_ap_wps_ie, - .set_freq = madwifi_set_freq, -}; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 7af3317775ab..4953af6a16b2 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -1074,8 +1074,8 @@ wpa_driver_ndis_associate(void *priv, /* Try to continue anyway */ } - if (params->key_mgmt_suite == KEY_MGMT_NONE || - params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { + 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++) { @@ -1102,12 +1102,12 @@ wpa_driver_ndis_associate(void *priv, priv_mode = Ndis802_11PrivFilterAcceptAll; } else if (params->wpa_ie[0] == WLAN_EID_RSN) { priv_mode = Ndis802_11PrivFilter8021xWEP; - if (params->key_mgmt_suite == KEY_MGMT_PSK) + 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 == KEY_MGMT_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) { @@ -1129,35 +1129,35 @@ wpa_driver_ndis_associate(void *priv, #endif /* CONFIG_WPS */ } else { priv_mode = Ndis802_11PrivFilter8021xWEP; - if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE) auth_mode = Ndis802_11AuthModeWPANone; - else if (params->key_mgmt_suite == KEY_MGMT_PSK) + else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPAPSK; else auth_mode = Ndis802_11AuthModeWPA; } switch (params->pairwise_suite) { - case CIPHER_CCMP: + case WPA_CIPHER_CCMP: encr = Ndis802_11Encryption3Enabled; break; - case CIPHER_TKIP: + case WPA_CIPHER_TKIP: encr = Ndis802_11Encryption2Enabled; break; - case CIPHER_WEP40: - case CIPHER_WEP104: + case WPA_CIPHER_WEP40: + case WPA_CIPHER_WEP104: encr = Ndis802_11Encryption1Enabled; break; - case CIPHER_NONE: + case WPA_CIPHER_NONE: #ifdef CONFIG_WPS if (params->wps == WPS_MODE_PRIVACY) { encr = Ndis802_11Encryption1Enabled; break; } #endif /* CONFIG_WPS */ - if (params->group_suite == CIPHER_CCMP) + if (params->group_suite == WPA_CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; - else if (params->group_suite == CIPHER_TKIP) + else if (params->group_suite == WPA_CIPHER_TKIP) encr = Ndis802_11Encryption2Enabled; else encr = Ndis802_11EncryptionDisabled; @@ -2110,14 +2110,8 @@ static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) dlen = dpos - desc; else dlen = os_strlen(desc); - drv->adapter_desc = os_malloc(dlen + 1); - if (drv->adapter_desc) { - os_memcpy(drv->adapter_desc, desc, dlen); - drv->adapter_desc[dlen] = '\0'; - } - + drv->adapter_desc = dup_binstr(desc, dlen); os_free(b); - if (drv->adapter_desc == NULL) return -1; @@ -2284,14 +2278,8 @@ static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) } else { dlen = os_strlen(desc[i]); } - drv->adapter_desc = os_malloc(dlen + 1); - if (drv->adapter_desc) { - os_memcpy(drv->adapter_desc, desc[i], dlen); - drv->adapter_desc[dlen] = '\0'; - } - + drv->adapter_desc = dup_binstr(desc[i], dlen); os_free(names); - if (drv->adapter_desc == NULL) return -1; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 37b6be9951f4..d7438683d8f0 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -1,6 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * Copyright (c) 2003-2004, Instant802 Networks, Inc. * Copyright (c) 2005-2006, Devicescape Software, Inc. * Copyright (c) 2007, Johannes Berg @@ -11,68 +11,35 @@ */ #include "includes.h" -#include #include -#include #include #include #include -#include #include +#ifdef CONFIG_LIBNL3_ROUTE +#include +#endif /* CONFIG_LIBNL3_ROUTE */ #include #include -#include #include -#include "nl80211_copy.h" #include "common.h" #include "eloop.h" -#include "utils/list.h" +#include "common/qca-vendor.h" +#include "common/qca-vendor-attr.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "l2_packet/l2_packet.h" #include "netlink.h" +#include "linux_defines.h" #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" #include "rfkill.h" -#include "driver.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 +#include "driver_nl80211.h" -#ifdef ANDROID -#include "android_drv.h" -/* system/core/libnl_2 in AOSP does not include nla_put_u32() */ -int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) -{ - return nla_put(msg, attrtype, sizeof(uint32_t), &value); -} -#endif /* ANDROID */ -#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 -#else +#ifndef CONFIG_LIBNL20 /* * libnl 1.1 has a bug, it tries to allocate socket numbers densely * but when you free a socket again it will mess up its bitmap and @@ -115,6 +82,15 @@ static void nl80211_handle_destroy(struct nl_handle *handle) #endif /* CONFIG_LIBNL20 */ +#ifdef ANDROID +/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */ +#undef nl_socket_set_nonblocking +#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) + +#define genl_ctrl_resolve android_genl_ctrl_resolve +#endif /* ANDROID */ + + static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg) { struct nl_handle *handle; @@ -146,234 +122,150 @@ static void nl_destroy_handles(struct nl_handle **handle) } -#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 +#if __WORDSIZE == 64 +#define ELOOP_SOCKET_INVALID (intptr_t) 0x8888888888888889ULL +#else +#define ELOOP_SOCKET_INVALID (intptr_t) 0x88888889ULL #endif -struct nl80211_global { - struct dl_list interfaces; - int if_add_ifindex; - struct netlink_data *netlink; - struct nl_cb *nl_cb; - struct nl_handle *nl; - int nl80211_id; - int ioctl_sock; /* socket for ioctl() use */ +static void nl80211_register_eloop_read(struct nl_handle **handle, + eloop_sock_handler handler, + void *eloop_data) +{ +#ifdef CONFIG_LIBNL20 + /* + * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB) + * by default. It is possible to hit that limit in some cases where + * operations are blocked, e.g., with a burst of Deauthentication frames + * to hostapd and STA entry deletion. Try to increase the buffer to make + * this less likely to occur. + */ + if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not set nl_socket RX buffer size: %s", + strerror(errno)); + /* continue anyway with the default (smaller) buffer */ + } +#endif /* CONFIG_LIBNL20 */ - struct nl_handle *nl_event; -}; + nl_socket_set_nonblocking(*handle); + eloop_register_read_sock(nl_socket_get_fd(*handle), handler, + eloop_data, *handle); + *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); +} -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; +static void nl80211_destroy_eloop_handle(struct nl_handle **handle) +{ + *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + eloop_unregister_read_sock(nl_socket_get_fd(*handle)); + nl_destroy_handles(handle); +} - int wiphy_idx; -}; static void nl80211_global_deinit(void *priv); -static void wpa_driver_nl80211_deinit(void *priv); - -struct i802_bss { - struct wpa_driver_nl80211_data *drv; - struct i802_bss *next; - int ifindex; - 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; - - u8 addr[ETH_ALEN]; - - int freq; - - 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]; - void *ctx; - int ifindex; - int if_removed; - int if_disabled; - int ignore_if_down_event; - struct rfkill_data *rfkill; - struct wpa_driver_capa capa; - int has_capability; - - int operstate; - - int scan_complete_events; - - struct nl_cb *nl_cb; +static void nl80211_check_global(struct nl80211_global *global); - u8 auth_bssid[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - int associated; - u8 ssid[32]; - 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; - - 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; - -#ifdef HOSTAPD - int eapol_sock; /* socket for EAPOL frames */ - - int default_if_indices[16]; - int *if_indices; - int num_if_indices; - - int last_freq; - int last_freq_ht; -#endif /* HOSTAPD */ - - /* From failed authentication command */ - int auth_freq; - u8 auth_bssid_[ETH_ALEN]; - u8 auth_ssid[32]; - 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; -}; +static void wpa_driver_nl80211_deinit(struct i802_bss *bss); +static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, + struct hostapd_freq_params *freq); - -static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, - void *timeout_ctx); -static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, - enum nl80211_iftype nlmode); static int -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); -static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code, - int local_state_change); -static void nl80211_remove_monitor_interface( - struct wpa_driver_nl80211_data *drv); +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, + const u8 *set_addr, int first, + const char *driver_params); static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie, int no_cck, int no_ack, int offchanok); -static int wpa_driver_nl80211_probe_req_report(void *priv, int report); -#ifdef ANDROID -static int android_pno_start(struct i802_bss *bss, - struct wpa_driver_scan_params *params); -static int android_pno_stop(struct i802_bss *bss); -#endif /* ANDROID */ +static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, + int report); -#ifdef HOSTAPD static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static int wpa_driver_nl80211_if_remove(void *priv, - enum wpa_driver_if_type type, - const char *ifname); -#else /* HOSTAPD */ -static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) -{ -} -static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static int nl80211_set_channel(struct i802_bss *bss, + struct hostapd_freq_params *freq, int set_chan); +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled); + +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, + int reset_mode); + +static int i802_set_iface_flags(struct i802_bss *bss, int up); +static int nl80211_set_param(void *priv, const char *param); + + +/* Converts nl80211_chan_width to a common format */ +enum chan_width convert2width(int width) { + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return CHAN_WIDTH_20_NOHT; + case NL80211_CHAN_WIDTH_20: + return CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_80P80: + return CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_160: + return CHAN_WIDTH_160; + } + return CHAN_WIDTH_UNKNOWN; } -static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) + +int is_ap_interface(enum nl80211_iftype nlmode) { - return 0; + return nlmode == NL80211_IFTYPE_AP || + nlmode == NL80211_IFTYPE_P2P_GO; } -#endif /* HOSTAPD */ -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); -static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, - int ifindex, int disabled); -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); -static int wpa_driver_nl80211_authenticate_retry( - struct wpa_driver_nl80211_data *drv); +int is_sta_interface(enum nl80211_iftype nlmode) +{ + return nlmode == NL80211_IFTYPE_STATION || + nlmode == NL80211_IFTYPE_P2P_CLIENT; +} -static int is_ap_interface(enum nl80211_iftype nlmode) +static int is_p2p_net_interface(enum nl80211_iftype nlmode) { - return (nlmode == NL80211_IFTYPE_AP || - nlmode == NL80211_IFTYPE_P2P_GO); + return nlmode == NL80211_IFTYPE_P2P_CLIENT || + nlmode == NL80211_IFTYPE_P2P_GO; } -static int is_sta_interface(enum nl80211_iftype nlmode) +struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex) { - return (nlmode == NL80211_IFTYPE_STATION || - nlmode == NL80211_IFTYPE_P2P_CLIENT); + struct i802_bss *bss; + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (bss->ifindex == ifindex) + return bss; + } + + return NULL; } -static int is_p2p_interface(enum nl80211_iftype nlmode) +static int is_mesh_interface(enum nl80211_iftype nlmode) { - return (nlmode == NL80211_IFTYPE_P2P_CLIENT || - nlmode == NL80211_IFTYPE_P2P_GO); + return nlmode == NL80211_IFTYPE_MESH_POINT; } -struct nl80211_bss_info_arg { - struct wpa_driver_nl80211_data *drv; - struct wpa_scan_results *res; - unsigned int assoc_freq; - u8 assoc_bssid[ETH_ALEN]; -}; - -static int bss_info_handler(struct nl_msg *msg, void *arg); +void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) +{ + if (drv->associated) + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + drv->associated = 0; + os_memset(drv->bssid, 0, ETH_ALEN); +} /* nl80211 code */ @@ -406,6 +298,28 @@ static int no_seq_check(struct nl_msg *msg, void *arg) } +static void nl80211_nlmsg_clear(struct nl_msg *msg) +{ + /* + * Clear nlmsg data, e.g., to make sure key material is not left in + * heap memory for unnecessarily long time. + */ + if (msg) { + struct nlmsghdr *hdr = nlmsg_hdr(msg); + void *data = nlmsg_data(hdr); + /* + * This would use nlmsg_datalen() or the older nlmsg_len() if + * only libnl were to maintain a stable API.. Neither will work + * with all released versions, so just calculate the length + * here. + */ + int len = hdr->nlmsg_len - NLMSG_HDRLEN; + + os_memset(data, 0, len); + } +} + + static int send_and_recv(struct nl80211_global *global, struct nl_handle *nl_handle, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), @@ -414,6 +328,9 @@ static int send_and_recv(struct nl80211_global *global, struct nl_cb *cb; int err = -ENOMEM; + if (!msg) + return -ENOMEM; + cb = nl_cb_clone(global->nl_cb); if (!cb) goto out; @@ -432,29 +349,27 @@ static int send_and_recv(struct nl80211_global *global, nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); - while (err > 0) - nl_recvmsgs(nl_handle, cb); + while (err > 0) { + int res = nl_recvmsgs(nl_handle, cb); + if (res < 0) { + wpa_printf(MSG_INFO, + "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } + } out: nl_cb_put(cb); + if (!valid_handler && valid_data == (void *) -1) + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return err; } -static int send_and_recv_msgs_global(struct nl80211_global *global, - struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data) -{ - return send_and_recv(global, global->nl, msg, valid_handler, - valid_data); -} - - -static 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 send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) { return send_and_recv(drv->global, drv->global->nl, msg, valid_handler, valid_data); @@ -502,37 +417,97 @@ static int nl_get_multicast_id(struct nl80211_global *global, const char *family, const char *group) { struct nl_msg *msg; - int ret = -1; + int ret; struct family_data res = { group, -ENOENT }; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), - 0, 0, CTRL_CMD_GETFAMILY, 0); - NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + if (!genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0) || + nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) { + nlmsg_free(msg); + return -1; + } - ret = send_and_recv_msgs_global(global, msg, family_handler, &res); - msg = NULL; + ret = send_and_recv(global, global->nl, msg, family_handler, &res); if (ret == 0) ret = res.id; - -nla_put_failure: - nlmsg_free(msg); return ret; } -static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, - struct nl_msg *msg, int flags, uint8_t cmd) +void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd) { return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, flags, cmd, 0); } +static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) +{ + if (bss->wdev_id_set) + return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); +} + + +struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (!nl80211_cmd(bss->drv, msg, flags, cmd) || + nl80211_set_iface_id(msg, bss) < 0) { + nlmsg_free(msg); + return NULL; + } + + return msg; +} + + +static struct nl_msg * +nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex, + int flags, uint8_t cmd) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (!nl80211_cmd(drv, msg, flags, cmd) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) { + nlmsg_free(msg); + return NULL; + } + + return msg; +} + + +struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags, + uint8_t cmd) +{ + return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd); +} + + +struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd) +{ + return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd); +} + + struct wiphy_idx_data { int wiphy_idx; + enum nl80211_iftype nlmode; + u8 *macaddr; }; @@ -548,59 +523,87 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg) if (tb[NL80211_ATTR_WIPHY]) info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + if (tb[NL80211_ATTR_IFTYPE]) + info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); + + if (tb[NL80211_ATTR_MAC] && info->macaddr) + os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + return NL_SKIP; } -static int nl80211_get_wiphy_index(struct i802_bss *bss) +int nl80211_get_wiphy_index(struct i802_bss *bss) { struct nl_msg *msg; struct wiphy_idx_data data = { .wiphy_idx = -1, + .macaddr = NULL, }; - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE))) return -1; - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) return data.wiphy_idx; - msg = NULL; -nla_put_failure: - nlmsg_free(msg); return -1; } +static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .nlmode = NL80211_IFTYPE_UNSPECIFIED, + .macaddr = NULL, + }; + + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE))) + return NL80211_IFTYPE_UNSPECIFIED; + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.nlmode; + return NL80211_IFTYPE_UNSPECIFIED; +} + + +static int nl80211_get_macaddr(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .macaddr = bss->addr, + }; + + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE))) + return -1; + + return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); +} + + static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, struct nl80211_wiphy_data *w) { struct nl_msg *msg; - int ret = -1; + int ret; msg = nlmsg_alloc(); if (!msg) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS); - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx); + if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) || + nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) { + nlmsg_free(msg); + return -1; + } ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register beacons command " "failed: ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; } - ret = 0; -nla_put_failure: - nlmsg_free(msg); return ret; } @@ -608,10 +611,15 @@ nla_put_failure: static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle) { struct nl80211_wiphy_data *w = eloop_ctx; + int res; wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available"); - nl_recvmsgs(handle, w->nl_cb); + res = nl_recvmsgs(handle, w->nl_cb); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } } @@ -695,8 +703,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) return NULL; } - eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons), - nl80211_recv_beacons, w, w->nl_beacons); + nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); dl_list_add(&nl80211_wiphys, &w->list); @@ -743,10 +750,9 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) if (!dl_list_empty(&w->bsss)) return; - eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons)); + nl80211_destroy_eloop_handle(&w->nl_beacons); nl_cb_put(w->nl_cb); - nl_destroy_handles(&w->nl_beacons); dl_list_del(&w->list); os_free(w); } @@ -774,48 +780,55 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) } -static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, - char *buf, size_t len, int del) +static void wpa_driver_nl80211_event_newlink( + struct wpa_driver_nl80211_data *drv, const char *ifname) { union wpa_event_data event; + if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (if_nametoindex(drv->first_bss->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", + drv->first_bss->ifname); + return; + } + if (!drv->if_removed) + return; + wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event", + drv->first_bss->ifname); + drv->if_removed = 0; + } + os_memset(&event, 0, sizeof(event)); - if (len > sizeof(event.interface_status.ifname)) - len = sizeof(event.interface_status.ifname) - 1; - os_memcpy(event.interface_status.ifname, buf, len); - event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : - EVENT_INTERFACE_ADDED; - - wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", - del ? "DEL" : "NEW", - event.interface_status.ifname, - del ? "removed" : "added"); - - if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) { - if (del) { - if (drv->if_removed) { - wpa_printf(MSG_DEBUG, "nl80211: if_removed " - "already set - ignore event"); - return; - } - drv->if_removed = 1; - } else { - if (if_nametoindex(drv->first_bss.ifname) == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Interface %s " - "does not exist - ignore " - "RTM_NEWLINK", - drv->first_bss.ifname); - return; - } - if (!drv->if_removed) { - wpa_printf(MSG_DEBUG, "nl80211: if_removed " - "already cleared - ignore event"); - return; - } - drv->if_removed = 0; + os_strlcpy(event.interface_status.ifname, ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static void wpa_driver_nl80211_event_dellink( + struct wpa_driver_nl80211_data *drv, const char *ifname) +{ + union wpa_event_data event; + + if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", + ifname); + return; } + wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1", + ifname); + drv->if_removed = 1; + } else { + wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed", + ifname); } + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } @@ -832,8 +845,8 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { if (attr->rta_type == IFLA_IFNAME) { - if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname) - == 0) + if (os_strcmp(((char *) attr) + rta_len, + drv->first_bss->ifname) == 0) return 1; else break; @@ -852,10 +865,10 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, return 1; if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { - drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname); + nl80211_check_global(drv->global); wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); - wpa_driver_nl80211_finish_drv_init(drv); + wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL); return 1; } @@ -883,36 +896,79 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, { struct nl80211_global *global = ctx; struct wpa_driver_nl80211_data *drv; - int attrlen, rta_len; + int attrlen; struct rtattr *attr; u32 brid = 0; char namebuf[IFNAMSIZ]; + char ifname[IFNAMSIZ + 1]; + char extra[100], *pos, *end; drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " - "ifindex %d", ifi->ifi_index); + wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d", + ifi->ifi_index); return; } - wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " - "(%s%s%s%s)", - drv->operstate, ifi->ifi_flags, + extra[0] = '\0'; + pos = extra; + end = pos + sizeof(extra); + ifname[0] = '\0'; + + attrlen = len; + attr = (struct rtattr *) buf; + while (RTA_OK(attr, attrlen)) { + switch (attr->rta_type) { + case IFLA_IFNAME: + if (RTA_PAYLOAD(attr) >= IFNAMSIZ) + break; + os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); + ifname[RTA_PAYLOAD(attr)] = '\0'; + break; + case IFLA_MASTER: + brid = nla_get_u32((struct nlattr *) attr); + pos += os_snprintf(pos, end - pos, " master=%u", brid); + break; + case IFLA_WIRELESS: + pos += os_snprintf(pos, end - pos, " wext"); + break; + case IFLA_OPERSTATE: + pos += os_snprintf(pos, end - pos, " operstate=%u", + nla_get_u32((struct nlattr *) attr)); + break; + case IFLA_LINKMODE: + pos += os_snprintf(pos, end - pos, " linkmode=%u", + nla_get_u32((struct nlattr *) attr)); + break; + } + attr = RTA_NEXT(attr, attrlen); + } + extra[sizeof(extra) - 1] = '\0'; + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + ifi->ifi_index, ifname, extra, ifi->ifi_family, + ifi->ifi_flags, (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && - linux_iface_up(drv->global->ioctl_sock, - drv->first_bss.ifname) > 0) { + linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); + drv->ignore_if_down_event = 0; return; } - wpa_printf(MSG_DEBUG, "nl80211: Interface down"); - if (drv->ignore_if_down_event) { + wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)", + namebuf, ifname); + if (os_strcmp(drv->first_bss->ifname, ifname) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Not the main interface (%s) - do not indicate interface down", + drv->first_bss->ifname); + } else if (drv->ignore_if_down_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event generated by mode change"); drv->ignore_if_down_event = 0; @@ -920,25 +976,56 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, drv->if_disabled = 1; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + + /* + * Try to get drv again, since it may be removed as + * part of the EVENT_INTERFACE_DISABLED handling for + * dynamic interfaces + */ + drv = nl80211_find_drv(global, ifi->ifi_index, + buf, len); + if (!drv) + return; } } if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { if (if_indextoname(ifi->ifi_index, namebuf) && - linux_iface_up(drv->global->ioctl_sock, - drv->first_bss.ifname) == 0) { + linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); - } else if (if_nametoindex(drv->first_bss.ifname) == 0) { + } else if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s does not exist", - drv->first_bss.ifname); + drv->first_bss->ifname); } else if (drv->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is marked " - "removed", drv->first_bss.ifname); + "removed", drv->first_bss->ifname); } else { + struct i802_bss *bss; + u8 addr[ETH_ALEN]; + + /* Re-read MAC address as it may have changed */ + bss = get_bss_ifindex(drv, ifi->ifi_index); + if (bss && + linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: %s: failed to re-read MAC address", + bss->ifname); + } else if (bss && + os_memcmp(addr, bss->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Own MAC address on ifindex %d (%s) changed from " + MACSTR " to " MACSTR, + ifi->ifi_index, bss->ifname, + MAC2STR(bss->addr), + MAC2STR(addr)); + os_memcpy(bss->addr, addr, ETH_ALEN); + } + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, @@ -954,30 +1041,35 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, */ if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && - !(ifi->ifi_flags & IFF_RUNNING)) + !(ifi->ifi_flags & IFF_RUNNING)) { + wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate"); netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, IF_OPER_UP); - - attrlen = len; - attr = (struct rtattr *) buf; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 0); - } else if (attr->rta_type == IFLA_MASTER) - brid = nla_get_u32((struct nlattr *) attr); - attr = RTA_NEXT(attr, attrlen); } + if (ifname[0]) + wpa_driver_nl80211_event_newlink(drv, ifname); + if (ifi->ifi_family == AF_BRIDGE && brid) { + struct i802_bss *bss; + /* device has been added to bridge */ - if_indextoname(brid, namebuf); + if (!if_indextoname(brid, namebuf)) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not find bridge ifname for ifindex %u", + brid); + return; + } wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); add_ifidx(drv, brid); + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (os_strcmp(ifname, bss->ifname) == 0) { + os_strlcpy(bss->brname, namebuf, IFNAMSIZ); + break; + } + } } } @@ -988,8251 +1080,7355 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, { struct nl80211_global *global = ctx; struct wpa_driver_nl80211_data *drv; - int attrlen, rta_len; + int attrlen; struct rtattr *attr; u32 brid = 0; + char ifname[IFNAMSIZ + 1]; + char extra[100], *pos, *end; drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for " - "foreign ifindex %d", ifi->ifi_index); + wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d", + ifi->ifi_index); return; } + extra[0] = '\0'; + pos = extra; + end = pos + sizeof(extra); + ifname[0] = '\0'; + attrlen = len; attr = (struct rtattr *) buf; - - rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 1); - } else if (attr->rta_type == IFLA_MASTER) + switch (attr->rta_type) { + case IFLA_IFNAME: + if (RTA_PAYLOAD(attr) >= IFNAMSIZ) + break; + os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); + ifname[RTA_PAYLOAD(attr)] = '\0'; + break; + case IFLA_MASTER: brid = nla_get_u32((struct nlattr *) attr); + pos += os_snprintf(pos, end - pos, " master=%u", brid); + break; + case IFLA_OPERSTATE: + pos += os_snprintf(pos, end - pos, " operstate=%u", + nla_get_u32((struct nlattr *) attr)); + break; + case IFLA_LINKMODE: + pos += os_snprintf(pos, end - pos, " linkmode=%u", + nla_get_u32((struct nlattr *) attr)); + break; + } attr = RTA_NEXT(attr, attrlen); } + extra[sizeof(extra) - 1] = '\0'; + + wpa_printf(MSG_DEBUG, "RTM_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + ifi->ifi_index, ifname, extra, ifi->ifi_family, + ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid)) + wpa_driver_nl80211_event_dellink(drv, ifname); if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; - if_indextoname(brid, namebuf); - wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " - "%s", brid, namebuf); - del_ifidx(drv, brid); - } -} - - -static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len) -{ - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - - wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); - mgmt = (const struct ieee80211_mgmt *) frame; - if (len < 24 + sizeof(mgmt->u.auth)) { - wpa_printf(MSG_DEBUG, "nl80211: Too short association event " - "frame"); - return; - } - os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); - os_memset(&event, 0, sizeof(event)); - os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); - event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); - event.auth.auth_transaction = - le_to_host16(mgmt->u.auth.auth_transaction); - event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); - if (len > 24 + sizeof(mgmt->u.auth)) { - event.auth.ies = mgmt->u.auth.variable; - event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); + if (!if_indextoname(brid, namebuf)) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not find bridge ifname for ifindex %u", + brid); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Remove ifindex %u for bridge %s", + brid, namebuf); + } + del_ifidx(drv, brid); } - - wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); } -static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) +unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; int ret; struct nl80211_bss_info_arg arg; + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN); os_memset(&arg, 0, sizeof(arg)); - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - arg.drv = drv; ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); - msg = NULL; if (ret == 0) { + unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ? + arg.ibss_freq : arg.assoc_freq; wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the " - "associated BSS from scan results: %u MHz", - arg.assoc_freq); - return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq; + "associated BSS from scan results: %u MHz", freq); + if (freq) + drv->assoc_freq = freq; + return drv->assoc_freq; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " "(%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); return drv->assoc_freq; } -static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len) +static int get_link_signal(struct nl_msg *msg, void *arg) { - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 status; - - wpa_printf(MSG_DEBUG, "nl80211: Associate event"); - mgmt = (const struct ieee80211_mgmt *) frame; - if (len < 24 + sizeof(mgmt->u.assoc_resp)) { - wpa_printf(MSG_DEBUG, "nl80211: Too short association event " - "frame"); - return; - } + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, + }; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + struct wpa_signal_info *sig_change = arg; - status = le_to_host16(mgmt->u.assoc_resp.status_code); - if (status != WLAN_STATUS_SUCCESS) { - os_memset(&event, 0, sizeof(event)); - event.assoc_reject.bssid = mgmt->bssid; - if (len > 24 + sizeof(mgmt->u.assoc_resp)) { - event.assoc_reject.resp_ies = - (u8 *) mgmt->u.assoc_resp.variable; - event.assoc_reject.resp_ies_len = - len - 24 - sizeof(mgmt->u.assoc_resp); - } - event.assoc_reject.status_code = status; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; - wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); - return; - } + sig_change->current_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); - drv->associated = 1; - os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); + if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) + sig_change->avg_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]); + else + sig_change->avg_signal = 0; - os_memset(&event, 0, sizeof(event)); - if (len > 24 + sizeof(mgmt->u.assoc_resp)) { - event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; - event.assoc_info.resp_ies_len = - len - 24 - sizeof(mgmt->u.assoc_resp); + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, + sinfo[NL80211_STA_INFO_TX_BITRATE], + rate_policy)) { + sig_change->current_txrate = 0; + } else { + if (rinfo[NL80211_RATE_INFO_BITRATE]) { + sig_change->current_txrate = + nla_get_u16(rinfo[ + NL80211_RATE_INFO_BITRATE]) * 100; + } + } } - event.assoc_info.freq = drv->assoc_freq; - - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); + return NL_SKIP; } -static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, - enum nl80211_commands cmd, struct nlattr *status, - struct nlattr *addr, struct nlattr *req_ie, - struct nlattr *resp_ie) +int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) { - union wpa_event_data event; - - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - /* - * Avoid reporting two association events that would confuse - * the core code. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " - "when using userspace SME", cmd); - return; - } - - if (cmd == NL80211_CMD_CONNECT) - wpa_printf(MSG_DEBUG, "nl80211: Connect event"); - else if (cmd == NL80211_CMD_ROAM) - wpa_printf(MSG_DEBUG, "nl80211: Roam event"); - - os_memset(&event, 0, sizeof(event)); - if (cmd == NL80211_CMD_CONNECT && - nla_get_u16(status) != WLAN_STATUS_SUCCESS) { - if (addr) - event.assoc_reject.bssid = nla_data(addr); - if (resp_ie) { - event.assoc_reject.resp_ies = nla_data(resp_ie); - event.assoc_reject.resp_ies_len = nla_len(resp_ie); - } - event.assoc_reject.status_code = nla_get_u16(status); - wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); - return; - } + struct nl_msg *msg; - drv->associated = 1; - if (addr) - os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); + sig->current_signal = -9999; + sig->current_txrate = 0; - if (req_ie) { - event.assoc_info.req_ies = nla_data(req_ie); - event.assoc_info.req_ies_len = nla_len(req_ie); - } - if (resp_ie) { - event.assoc_info.resp_ies = nla_data(resp_ie); - event.assoc_info.resp_ies_len = nla_len(resp_ie); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) { + nlmsg_free(msg); + return -ENOBUFS; } - event.assoc_info.freq = nl80211_get_assoc_freq(drv); - - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); + return send_and_recv_msgs(drv, msg, get_link_signal, sig); } -static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, - struct nlattr *reason, struct nlattr *addr, - struct nlattr *by_ap) +static int get_link_noise(struct nl_msg *msg, void *arg) { - union wpa_event_data data; - unsigned int locally_generated = by_ap == NULL; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_signal_info *sig_change = arg; - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - /* - * Avoid reporting two disassociation events that could - * confuse the core code. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " - "event when using userspace SME"); - return; - } + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - if (drv->ignore_next_local_disconnect) { - drv->ignore_next_local_disconnect = 0; - if (locally_generated) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " - "event triggered during reassociation"); - return; - } - wpa_printf(MSG_WARNING, "nl80211: Was expecting local " - "disconnect but got another disconnect " - "event first"); + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: survey data missing!"); + return NL_SKIP; } - wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); - drv->associated = 0; - os_memset(&data, 0, sizeof(data)); - if (reason) - data.deauth_info.reason_code = nla_get_u16(reason); - data.deauth_info.locally_generated = by_ap == NULL; - wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); -} - - -static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, struct nlattr *type) -{ - union wpa_event_data data; - int ht_enabled = 1; - int chan_offset = 0; + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested " + "attributes!"); + return NL_SKIP; + } - wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; - if (!freq || !type) - return; + if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + sig_change->frequency) + return NL_SKIP; - switch (nla_get_u32(type)) { - case NL80211_CHAN_NO_HT: - ht_enabled = 0; - break; - case NL80211_CHAN_HT20: - break; - case NL80211_CHAN_HT40PLUS: - chan_offset = 1; - break; - case NL80211_CHAN_HT40MINUS: - chan_offset = -1; - break; - } + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; - data.ch_switch.freq = nla_get_u32(freq); - data.ch_switch.ht_enabled = ht_enabled; - data.ch_switch.ch_offset = chan_offset; + sig_change->current_noise = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); - wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); + return NL_SKIP; } -static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, - enum nl80211_commands cmd, struct nlattr *addr) +int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change) { - union wpa_event_data event; - enum wpa_event_type ev; - - if (nla_len(addr) != ETH_ALEN) - return; - - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, - cmd, MAC2STR((u8 *) nla_data(addr))); + struct nl_msg *msg; - if (cmd == NL80211_CMD_AUTHENTICATE) - ev = EVENT_AUTH_TIMED_OUT; - else if (cmd == NL80211_CMD_ASSOCIATE) - ev = EVENT_ASSOC_TIMED_OUT; - else - return; + sig_change->current_noise = 9999; + sig_change->frequency = drv->assoc_freq; - os_memset(&event, 0, sizeof(event)); - os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); - wpa_supplicant_event(drv->ctx, ev, &event); + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); } -static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv, - struct nlattr *freq, struct nlattr *sig, - const u8 *frame, size_t len) +static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, + void *handle) { - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 fc, stype; - int ssi_signal = 0; - - wpa_printf(MSG_DEBUG, "nl80211: Frame event"); - mgmt = (const struct ieee80211_mgmt *) frame; - if (len < 24) { - wpa_printf(MSG_DEBUG, "nl80211: Too short action frame"); - return; - } - - fc = le_to_host16(mgmt->frame_control); - stype = WLAN_FC_GET_STYPE(fc); + struct nl_cb *cb = eloop_ctx; + int res; - if (sig) - ssi_signal = (s32) nla_get_u32(sig); + wpa_printf(MSG_MSGDUMP, "nl80211: Event message available"); - os_memset(&event, 0, sizeof(event)); - if (freq) { - event.rx_action.freq = nla_get_u32(freq); - drv->last_mgmt_freq = event.rx_action.freq; - } - if (stype == WLAN_FC_STYPE_ACTION) { - event.rx_action.da = mgmt->da; - event.rx_action.sa = mgmt->sa; - event.rx_action.bssid = mgmt->bssid; - event.rx_action.category = mgmt->u.action.category; - event.rx_action.data = &mgmt->u.action.category + 1; - event.rx_action.len = frame + len - event.rx_action.data; - wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); - } else { - event.rx_mgmt.frame = frame; - event.rx_mgmt.frame_len = len; - event.rx_mgmt.ssi_signal = ssi_signal; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + res = nl_recvmsgs(handle, cb); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); } } -static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, - struct nlattr *cookie, const u8 *frame, - size_t len, struct nlattr *ack) +/** + * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain + * @priv: driver_nl80211 private data + * @alpha2_arg: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This asks nl80211 to set the regulatory domain for given + * country ISO / IEC alpha2. + */ +static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) { - union wpa_event_data event; - const struct ieee80211_hdr *hdr; - u16 fc; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + char alpha2[3]; + struct nl_msg *msg; - wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); - if (!is_ap_interface(drv->nlmode)) { - u64 cookie_val; + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - if (!cookie) - return; + alpha2[0] = alpha2_arg[0]; + alpha2[1] = alpha2_arg[1]; + alpha2[2] = '\0'; - cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" - " cookie=0%llx%s (ack=%d)", - (long long unsigned int) cookie_val, - cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)", ack != NULL); - if (cookie_val != drv->send_action_cookie) - return; + if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) || + nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) { + nlmsg_free(msg); + return -EINVAL; } - - hdr = (const struct ieee80211_hdr *) frame; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = frame; - event.tx_status.data_len = len; - event.tx_status.ack = ack != NULL; - wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); + if (send_and_recv_msgs(drv, msg, NULL, NULL)) + return -EINVAL; + return 0; } -static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, - enum wpa_event_type type, - const u8 *frame, size_t len) +static int nl80211_get_country(struct nl_msg *msg, void *arg) { - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - const u8 *bssid = NULL; - u16 reason_code = 0; + char *alpha2 = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - if (type == EVENT_DEAUTH) - wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); - else - wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) { + wpa_printf(MSG_DEBUG, "nl80211: No country information available"); + return NL_SKIP; + } + os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3); + return NL_SKIP; +} - mgmt = (const struct ieee80211_mgmt *) frame; - if (len >= 24) { - bssid = mgmt->bssid; - if (drv->associated != 0 && - os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && - os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { - /* - * We have presumably received this deauth as a - * response to a clear_state_mismatch() outgoing - * deauth. Don't let it take us offline! - */ - wpa_printf(MSG_DEBUG, "nl80211: Deauth received " - "from Unknown BSSID " MACSTR " -- ignoring", - MAC2STR(bssid)); - return; - } - } +static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; - drv->associated = 0; - os_memset(&event, 0, sizeof(event)); + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - /* Note: Same offset for Reason Code in both frame subtypes */ - if (len >= 24 + sizeof(mgmt->u.deauth)) - reason_code = le_to_host16(mgmt->u.deauth.reason_code); - - if (type == EVENT_DISASSOC) { - event.disassoc_info.locally_generated = - !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN); - event.disassoc_info.addr = bssid; - event.disassoc_info.reason_code = reason_code; - if (frame + len > mgmt->u.disassoc.variable) { - event.disassoc_info.ie = mgmt->u.disassoc.variable; - event.disassoc_info.ie_len = frame + len - - mgmt->u.disassoc.variable; - } - } else { - event.deauth_info.locally_generated = - !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN); - event.deauth_info.addr = bssid; - event.deauth_info.reason_code = reason_code; - if (frame + len > mgmt->u.deauth.variable) { - event.deauth_info.ie = mgmt->u.deauth.variable; - event.deauth_info.ie_len = frame + len - - mgmt->u.deauth.variable; - } - } + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + alpha2[0] = '\0'; + ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2); + if (!alpha2[0]) + ret = -1; - wpa_supplicant_event(drv->ctx, type, &event); + return ret; } -static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, - enum wpa_event_type type, - const u8 *frame, size_t len) +static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) { - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 reason_code = 0; + int ret; - if (type == EVENT_UNPROT_DEAUTH) - wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); - else - wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); - - if (len < 24) - return; + global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (global->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + return -1; + } - mgmt = (const struct ieee80211_mgmt *) frame; + global->nl = nl_create_handle(global->nl_cb, "nl"); + if (global->nl == NULL) + goto err; - os_memset(&event, 0, sizeof(event)); - /* Note: Same offset for Reason Code in both frame subtypes */ - if (len >= 24 + sizeof(mgmt->u.deauth)) - reason_code = le_to_host16(mgmt->u.deauth.reason_code); - - if (type == EVENT_UNPROT_DISASSOC) { - event.unprot_disassoc.sa = mgmt->sa; - event.unprot_disassoc.da = mgmt->da; - event.unprot_disassoc.reason_code = reason_code; - } else { - event.unprot_deauth.sa = mgmt->sa; - event.unprot_deauth.da = mgmt->da; - event.unprot_deauth.reason_code = reason_code; + global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211"); + if (global->nl80211_id < 0) { + wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " + "found"); + goto err; } - wpa_supplicant_event(drv->ctx, type, &event); -} - + global->nl_event = nl_create_handle(global->nl_cb, "event"); + if (global->nl_event == NULL) + goto err; -static void mlme_event(struct wpa_driver_nl80211_data *drv, - enum nl80211_commands cmd, struct nlattr *frame, - struct nlattr *addr, struct nlattr *timed_out, - struct nlattr *freq, struct nlattr *ack, - struct nlattr *cookie, struct nlattr *sig) -{ - if (timed_out && addr) { - mlme_timeout_event(drv, cmd, addr); - return; + ret = nl_get_multicast_id(global, "nl80211", "scan"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for scan events: %d (%s)", + ret, strerror(-ret)); + goto err; } - if (frame == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame " - "data", cmd); - return; + ret = nl_get_multicast_id(global, "nl80211", "mlme"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for mlme events: %d (%s)", + ret, strerror(-ret)); + goto err; } - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd); - wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", - nla_data(frame), nla_len(frame)); - - switch (cmd) { - case NL80211_CMD_AUTHENTICATE: - mlme_event_auth(drv, nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_ASSOCIATE: - mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_DEAUTHENTICATE: - mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, - nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_DISASSOCIATE: - mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, - nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_FRAME: - mlme_event_mgmt(drv, freq, sig, nla_data(frame), - nla_len(frame)); - break; - case NL80211_CMD_FRAME_TX_STATUS: - mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), - nla_len(frame), ack); - break; - case NL80211_CMD_UNPROT_DEAUTHENTICATE: - mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, - nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_UNPROT_DISASSOCIATE: - mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, - nla_data(frame), nla_len(frame)); - break; - default: - break; + ret = nl_get_multicast_id(global, "nl80211", "regulatory"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for regulatory events: %d (%s)", + ret, strerror(-ret)); + /* Continue without regulatory events */ } -} + ret = nl_get_multicast_id(global, "nl80211", "vendor"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for vendor events: %d (%s)", + ret, strerror(-ret)); + /* Continue without vendor events */ + } -static void mlme_event_michael_mic_failure(struct i802_bss *bss, - struct nlattr *tb[]) -{ - union wpa_event_data data; + nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_global_event, global); - wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); - os_memset(&data, 0, sizeof(data)); - if (tb[NL80211_ATTR_MAC]) { - wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", - nla_data(tb[NL80211_ATTR_MAC]), - nla_len(tb[NL80211_ATTR_MAC])); - data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); - } - if (tb[NL80211_ATTR_KEY_SEQ]) { - wpa_hexdump(MSG_DEBUG, "nl80211: TSC", - nla_data(tb[NL80211_ATTR_KEY_SEQ]), - nla_len(tb[NL80211_ATTR_KEY_SEQ])); - } - if (tb[NL80211_ATTR_KEY_TYPE]) { - enum nl80211_key_type key_type = - nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); - wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); - if (key_type == NL80211_KEYTYPE_PAIRWISE) - data.michael_mic_failure.unicast = 1; - } else - data.michael_mic_failure.unicast = 1; + nl80211_register_eloop_read(&global->nl_event, + wpa_driver_nl80211_event_receive, + global->nl_cb); - if (tb[NL80211_ATTR_KEY_IDX]) { - u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); - wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); - } + return 0; - wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +err: + nl_destroy_handles(&global->nl_event); + nl_destroy_handles(&global->nl); + nl_cb_put(global->nl_cb); + global->nl_cb = NULL; + return -1; } -static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) +static void nl80211_check_global(struct nl80211_global *global) { - if (tb[NL80211_ATTR_MAC] == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " - "event"); - return; - } - os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - drv->associated = 1; - wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", - MAC2STR(drv->bssid)); + struct nl_handle *handle; + const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL }; + int ret; + unsigned int i; - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + /* + * Try to re-add memberships to handle case of cfg80211 getting reloaded + * and all registration having been cleared. + */ + handle = (void *) (((intptr_t) global->nl_event) ^ + ELOOP_SOCKET_INVALID); + + for (i = 0; groups[i]; i++) { + ret = nl_get_multicast_id(global, "nl80211", groups[i]); + if (ret >= 0) + ret = nl_socket_add_membership(handle, ret); + if (ret < 0) { + wpa_printf(MSG_INFO, + "nl80211: Could not re-add multicast membership for %s events: %d (%s)", + groups[i], ret, strerror(-ret)); + } + } } -static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, - int cancel_event, struct nlattr *tb[]) +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { - unsigned int freq, chan_type, duration; - union wpa_event_data data; - u64 cookie; - - if (tb[NL80211_ATTR_WIPHY_FREQ]) - freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); - else - freq = 0; - - if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) - chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - else - chan_type = 0; - - if (tb[NL80211_ATTR_DURATION]) - duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); - else - duration = 0; - - if (tb[NL80211_ATTR_COOKIE]) - cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); - else - cookie = 0; - - wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " - "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", - cancel_event, freq, chan_type, duration, - (long long unsigned int) cookie, - cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); - - if (cookie != drv->remain_on_chan_cookie) - return; /* not for us */ - - if (cancel_event) - drv->pending_remain_on_chan = 0; - - os_memset(&data, 0, sizeof(data)); - data.remain_on_channel.freq = freq; - data.remain_on_channel.duration = duration; - wpa_supplicant_event(drv->ctx, cancel_event ? - EVENT_CANCEL_REMAIN_ON_CHANNEL : - EVENT_REMAIN_ON_CHANNEL, &data); + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ } -static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, - struct nlattr *tb[]) +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) { - union wpa_event_data event; - struct nlattr *nl; - int rem; - struct scan_info *info; -#define MAX_REPORT_FREQS 50 - int freqs[MAX_REPORT_FREQS]; - int num_freqs = 0; - - if (drv->scan_for_auth) { - drv->scan_for_auth = 0; - wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " - "cfg80211 BSS entry"); - wpa_driver_nl80211_authenticate_retry(drv); + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (i802_set_iface_flags(drv->first_bss, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); return; } - - os_memset(&event, 0, sizeof(event)); - info = &event.scan_info; - info->aborted = aborted; - - if (tb[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { - struct wpa_driver_scan_ssid *s = - &info->ssids[info->num_ssids]; - s->ssid = nla_data(nl); - s->ssid_len = nla_len(nl); - info->num_ssids++; - if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) - break; - } - } - if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { - nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) - { - freqs[num_freqs] = nla_get_u32(nl); - num_freqs++; - if (num_freqs == MAX_REPORT_FREQS - 1) - break; - } - info->freqs = freqs; - info->num_freqs = num_freqs; - } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); + /* rtnetlink ifup handler will report interface as enabled */ } -static int get_link_signal(struct nl_msg *msg, void *arg) +static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, + void *eloop_ctx, + void *handle) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; - static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { - [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, - }; - struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; - static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { - [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, - [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, - [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, - [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, - }; - struct wpa_signal_info *sig_change = arg; + struct wpa_driver_nl80211_data *drv = eloop_ctx; + u8 data[2048]; + struct msghdr msg; + struct iovec entry; + u8 control[512]; + struct cmsghdr *cmsg; + int res, found_ee = 0, found_wifi = 0, acked = 0; + union wpa_event_data event; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (!tb[NL80211_ATTR_STA_INFO] || - nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, - tb[NL80211_ATTR_STA_INFO], policy)) - return NL_SKIP; - if (!sinfo[NL80211_STA_INFO_SIGNAL]) - return NL_SKIP; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); - sig_change->current_signal = - (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + res = recvmsg(sock, &msg, MSG_ERRQUEUE); + /* if error or not fitting 802.3 header, return */ + if (res < 14) + return; - if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { - if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, - sinfo[NL80211_STA_INFO_TX_BITRATE], - rate_policy)) { - sig_change->current_txrate = 0; - } else { - if (rinfo[NL80211_RATE_INFO_BITRATE]) { - sig_change->current_txrate = - nla_get_u16(rinfo[ - NL80211_RATE_INFO_BITRATE]) * 100; - } + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_WIFI_STATUS) { + int *ack; + + found_wifi = 1; + ack = (void *)CMSG_DATA(cmsg); + acked = *ack; + } + + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + + if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) + found_ee = 1; } } - return NL_SKIP; + if (!found_ee || !found_wifi) + return; + + memset(&event, 0, sizeof(event)); + event.eapol_tx_status.dst = data; + event.eapol_tx_status.data = data + 14; + event.eapol_tx_status.data_len = res - 14; + event.eapol_tx_status.ack = acked; + wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); } -static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, - struct wpa_signal_info *sig) +static int nl80211_init_bss(struct i802_bss *bss) { - struct nl_msg *msg; - - sig->current_signal = -9999; - sig->current_txrate = 0; + bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!bss->nl_cb) + return -1; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_bss_event, bss); - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); + return 0; +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); - return send_and_recv_msgs(drv, msg, get_link_signal, sig); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; +static void nl80211_destroy_bss(struct i802_bss *bss) +{ + nl_cb_put(bss->nl_cb); + bss->nl_cb = NULL; } -static int get_link_noise(struct nl_msg *msg, void *arg) +static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, + void *global_priv, int hostapd, + const u8 *set_addr, + const char *driver_params) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; - static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { - [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, - [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, - }; - struct wpa_signal_info *sig_change = arg; + struct wpa_driver_nl80211_data *drv; + struct rfkill_config *rcfg; + struct i802_bss *bss; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + if (global_priv == NULL) + return NULL; + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->global = global_priv; + drv->ctx = ctx; + drv->hostapd = !!hostapd; + drv->eapol_sock = -1; - if (!tb[NL80211_ATTR_SURVEY_INFO]) { - wpa_printf(MSG_DEBUG, "nl80211: survey data missing!"); - return NL_SKIP; - } + /* + * There is no driver capability flag for this, so assume it is + * supported and disable this on first attempt to use if the driver + * rejects the command due to missing support. + */ + drv->set_rekey_offload = 1; - if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, - tb[NL80211_ATTR_SURVEY_INFO], - survey_policy)) { - wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested " - "attributes!"); - return NL_SKIP; + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); + drv->if_indices = drv->default_if_indices; + + drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); + if (!drv->first_bss) { + os_free(drv); + return NULL; } + bss = drv->first_bss; + bss->drv = drv; + bss->ctx = ctx; - if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) - return NL_SKIP; + os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); + drv->monitor_ifidx = -1; + drv->monitor_sock = -1; + drv->eapol_tx_sock = -1; + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != - sig_change->frequency) - return NL_SKIP; + if (nl80211_init_bss(bss)) + goto failed; - if (!sinfo[NL80211_SURVEY_INFO_NOISE]) - return NL_SKIP; + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } - sig_change->current_noise = - (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) + drv->start_iface_up = 1; - return NL_SKIP; -} + if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params)) + goto failed; + drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->eapol_tx_sock < 0) + goto failed; -static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, - struct wpa_signal_info *sig_change) -{ - struct nl_msg *msg; + if (drv->data_tx_status) { + int enabled = 1; - sig_change->current_noise = 9999; - sig_change->frequency = drv->assoc_freq; + if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, + &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: wifi status sockopt failed\n"); + drv->data_tx_status = 0; + if (!drv->use_monitor) + drv->capa.flags &= + ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + } else { + eloop_register_read_sock(drv->eapol_tx_sock, + wpa_driver_nl80211_handle_eapol_tx_status, + drv, NULL); + } + } - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + if (drv->global) { + nl80211_check_global(drv->global); + dl_list_add(&drv->global->interfaces, &drv->list); + drv->in_interface_list = 1; + } - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + return bss; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); +failed: + wpa_driver_nl80211_deinit(bss); + return NULL; +} - return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + +/** + * wpa_driver_nl80211_init - Initialize nl80211 driver interface + * @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 + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, + void *global_priv) +{ + return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL, + NULL); } -static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +static int nl80211_register_frame(struct i802_bss *bss, + struct nl_handle *nl_handle, + u16 type, const u8 *match, size_t match_len) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; - static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { - [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, - [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, - }; - struct wpa_scan_results *scan_results = arg; - struct wpa_scan_res *scan_res; - size_t i; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + char buf[30]; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + buf[0] = '\0'; + wpa_snprintf_hex(buf, sizeof(buf), match, match_len); + wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s", + type, fc2str(type), nl_handle, buf); - if (!tb[NL80211_ATTR_SURVEY_INFO]) { - wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); - return NL_SKIP; + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_ACTION)) || + nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) || + nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) { + nlmsg_free(msg); + return -1; } - if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, - tb[NL80211_ATTR_SURVEY_INFO], - survey_policy)) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " - "attributes"); - return NL_SKIP; + ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register frame command " + "failed (type=%u): ret=%d (%s)", + type, ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", + match, match_len); } + return ret; +} - if (!sinfo[NL80211_SURVEY_INFO_NOISE]) - return NL_SKIP; - - if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) - return NL_SKIP; - for (i = 0; i < scan_results->num; ++i) { - scan_res = scan_results->res[i]; - if (!scan_res) - continue; - if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != - scan_res->freq) - continue; - if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) - continue; - scan_res->noise = (s8) - nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); - scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; +static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) +{ + if (bss->nl_mgmt) { + wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " + "already on! (nl_mgmt=%p)", bss->nl_mgmt); + return -1; } - return NL_SKIP; + bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt"); + if (bss->nl_mgmt == NULL) + return -1; + + return 0; } -static int nl80211_get_noise_for_scan_results( - struct wpa_driver_nl80211_data *drv, - struct wpa_scan_results *scan_res) +static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) { - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + nl80211_register_eloop_read(&bss->nl_mgmt, + wpa_driver_nl80211_event_receive, + bss->nl_cb); +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, - scan_res); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; +static int nl80211_register_action_frame(struct i802_bss *bss, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(bss, bss->nl_mgmt, + type, match, match_len); } -static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) +static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { - static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, - }; - struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; - enum nl80211_cqm_rssi_threshold_event event; - union wpa_event_data ed; - struct wpa_signal_info sig; - int res; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; - if (tb[NL80211_ATTR_CQM] == NULL || - nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], - cqm_policy)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); - return; - } + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " + "handle %p", bss->nl_mgmt); - os_memset(&ed, 0, sizeof(ed)); + if (drv->nlmode == NL80211_IFTYPE_ADHOC) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); - if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { - if (!tb[NL80211_ATTR_MAC]) - return; - os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), - ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); - return; + /* register for any AUTH message */ + nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0); } - if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) - return; - event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); - - if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { - wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " - "event: RSSI high"); - ed.signal_change.above_threshold = 1; - } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { - wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " - "event: RSSI low"); - ed.signal_change.above_threshold = 0; - } else - return; - - res = nl80211_get_link_signal(drv, &sig); - if (res == 0) { - ed.signal_change.current_signal = sig.current_signal; - ed.signal_change.current_txrate = sig.current_txrate; - wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", - sig.current_signal, sig.current_txrate); +#ifdef CONFIG_INTERWORKING + /* QoS Map Configure */ + if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) + ret = -1; +#endif /* CONFIG_INTERWORKING */ +#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) + /* GAS Initial Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) + ret = -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) + ret = -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) + ret = -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) + ret = -1; + /* Protected GAS Initial Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0) + ret = -1; + /* Protected GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0) + ret = -1; + /* Protected GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0) + ret = -1; + /* Protected GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0) + ret = -1; +#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + /* P2P Public Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + ret = -1; + /* P2P Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + ret = -1; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211W + /* SA Query Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_TDLS + if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { + /* TDLS Discovery Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) < + 0) + ret = -1; } +#endif /* CONFIG_TDLS */ - res = nl80211_get_link_noise(drv, &sig); - if (res == 0) { - ed.signal_change.current_noise = sig.current_noise; - wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", - sig.current_noise); - } + /* FT Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) + ret = -1; + else + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; - wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); -} + /* WNM - BSS Transition Management Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) + ret = -1; + /* WNM-Sleep Mode Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) + ret = -1; +#ifdef CONFIG_HS20 + /* WNM-Notification */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0) + ret = -1; +#endif /* CONFIG_HS20 */ -static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - u8 *addr; - union wpa_event_data data; + /* WMM-AC ADDTS Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0) + ret = -1; - if (tb[NL80211_ATTR_MAC] == NULL) - return; - addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); - - if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - u8 *ies = NULL; - size_t ies_len = 0; - if (tb[NL80211_ATTR_IE]) { - ies = nla_data(tb[NL80211_ATTR_IE]); - ies_len = nla_len(tb[NL80211_ATTR_IE]); - } - wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); - drv_event_assoc(drv->ctx, addr, ies, ies_len, 0); - return; - } + /* WMM-AC DELTS */ + if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0) + ret = -1; - if (drv->nlmode != NL80211_IFTYPE_ADHOC) - return; + /* Radio Measurement - Neighbor Report Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0) + ret = -1; - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data); + /* Radio Measurement - Link Measurement Request */ + if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) && + (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0)) + ret = -1; + + nl80211_mgmt_handle_register_eloop(bss); + + return ret; } -static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) +static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss) { - u8 *addr; - union wpa_event_data data; + int ret = 0; - if (tb[NL80211_ATTR_MAC] == NULL) - return; - addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, - MAC2STR(addr)); + if (nl80211_alloc_mgmt_handle(bss)) + return -1; - if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - drv_event_disassoc(drv->ctx, addr); - return; - } + wpa_printf(MSG_DEBUG, + "nl80211: Subscribe to mgmt frames with mesh handle %p", + bss->nl_mgmt); - if (drv->nlmode != NL80211_IFTYPE_ADHOC) - return; + /* Auth frames for mesh SAE */ + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4), + NULL, 0) < 0) + ret = -1; - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); -} - - -static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; - static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { - [NL80211_REKEY_DATA_KEK] = { - .minlen = NL80211_KEK_LEN, - .maxlen = NL80211_KEK_LEN, - }, - [NL80211_REKEY_DATA_KCK] = { - .minlen = NL80211_KCK_LEN, - .maxlen = NL80211_KCK_LEN, - }, - [NL80211_REKEY_DATA_REPLAY_CTR] = { - .minlen = NL80211_REPLAY_CTR_LEN, - .maxlen = NL80211_REPLAY_CTR_LEN, - }, - }; - union wpa_event_data data; + /* Mesh peering open */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0) + ret = -1; + /* Mesh peering confirm */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0) + ret = -1; + /* Mesh peering close */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0) + ret = -1; - if (!tb[NL80211_ATTR_MAC]) - return; - if (!tb[NL80211_ATTR_REKEY_DATA]) - return; - if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, - tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) - return; - if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) - return; + nl80211_mgmt_handle_register_eloop(bss); - os_memset(&data, 0, sizeof(data)); - data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, - MAC2STR(data.driver_gtk_rekey.bssid)); - data.driver_gtk_rekey.replay_ctr = - nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); - wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", - data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); - wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); -} - - -static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; - static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { - [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, - [NL80211_PMKSA_CANDIDATE_BSSID] = { - .minlen = ETH_ALEN, - .maxlen = ETH_ALEN, - }, - [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, - }; - union wpa_event_data data; + return ret; +} - wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); - if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) - return; - if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, - tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) - return; - if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || - !cand[NL80211_PMKSA_CANDIDATE_BSSID]) - return; +static int nl80211_register_spurious_class3(struct i802_bss *bss) +{ + struct nl_msg *msg; + int ret; - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.pmkid_candidate.bssid, - nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); - data.pmkid_candidate.index = - nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); - data.pmkid_candidate.preauth = - cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; - wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME); + ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " + "failed: ret=%d (%s)", + ret, strerror(-ret)); + } + return ret; } -static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) +static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) { - union wpa_event_data data; + static const int stypes[] = { + WLAN_FC_STYPE_AUTH, + WLAN_FC_STYPE_ASSOC_REQ, + WLAN_FC_STYPE_REASSOC_REQ, + WLAN_FC_STYPE_DISASSOC, + WLAN_FC_STYPE_DEAUTH, + WLAN_FC_STYPE_ACTION, + WLAN_FC_STYPE_PROBE_REQ, +/* Beacon doesn't work as mac80211 doesn't currently allow + * it, but it wouldn't really be the right thing anyway as + * it isn't per interface ... maybe just dump the scan + * results periodically for OLBC? + */ + /* WLAN_FC_STYPE_BEACON, */ + }; + unsigned int i; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p", bss->nl_mgmt); - wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); + for (i = 0; i < ARRAY_SIZE(stypes); i++) { + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (stypes[i] << 4), + NULL, 0) < 0) { + goto out_err; + } + } - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) - return; + if (nl80211_register_spurious_class3(bss)) + goto out_err; - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.client_poll.addr, - nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + if (nl80211_get_wiphy_data_ap(bss) == NULL) + goto out_err; + + nl80211_mgmt_handle_register_eloop(bss); + return 0; - wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); +out_err: + nl_destroy_handles(&bss->nl_mgmt); + return -1; } -static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) +static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) { - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p (device SME)", bss->nl_mgmt); - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) - return; + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_ACTION << 4), + NULL, 0) < 0) + goto out_err; - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { - case NL80211_TDLS_SETUP: - wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " - MACSTR, MAC2STR(data.tdls.peer)); - data.tdls.oper = TDLS_REQUEST_SETUP; - break; - case NL80211_TDLS_TEARDOWN: - wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " - MACSTR, MAC2STR(data.tdls.peer)); - data.tdls.oper = TDLS_REQUEST_TEARDOWN; - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " - "event"); - return; - } - if (tb[NL80211_ATTR_REASON_CODE]) { - data.tdls.reason_code = - nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); - } + nl80211_mgmt_handle_register_eloop(bss); + return 0; - wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); +out_err: + nl_destroy_handles(&bss->nl_mgmt); + return -1; } -static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, - int wds) +static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) { - struct wpa_driver_nl80211_data *drv = bss->drv; - union wpa_event_data event; - - if (!tb[NL80211_ATTR_MAC]) + if (bss->nl_mgmt == NULL) return; + wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " + "(%s)", bss->nl_mgmt, reason); + nl80211_destroy_eloop_handle(&bss->nl_mgmt); - os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.bssid = bss->addr; - event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); - event.rx_from_unknown.wds = wds; - - wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); + nl80211_put_wiphy_data_ap(bss); } -static void do_process_drv_event(struct i802_bss *bss, int cmd, - struct nlattr **tb) +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) { - struct wpa_driver_nl80211_data *drv = bss->drv; - - if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && - (cmd == NL80211_CMD_NEW_SCAN_RESULTS || - cmd == NL80211_CMD_SCAN_ABORTED)) { - wpa_driver_nl80211_set_mode(&drv->first_bss, - drv->ap_scan_as_station); - drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - } - - switch (cmd) { - case NL80211_CMD_TRIGGER_SCAN: - wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); - break; - case NL80211_CMD_START_SCHED_SCAN: - wpa_printf(MSG_DEBUG, "nl80211: Sched scan started"); - break; - case NL80211_CMD_SCHED_SCAN_STOPPED: - wpa_printf(MSG_DEBUG, "nl80211: Sched scan stopped"); - wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); - break; - case NL80211_CMD_NEW_SCAN_RESULTS: - wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); - drv->scan_complete_events = 1; - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 0, tb); - break; - case NL80211_CMD_SCHED_SCAN_RESULTS: - wpa_printf(MSG_DEBUG, - "nl80211: New sched scan results available"); - send_scan_event(drv, 0, tb); - break; - case NL80211_CMD_SCAN_ABORTED: - wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); - /* - * Need to indicate that scan results are available in order - * not to make wpa_supplicant stop its scanning. - */ - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 1, tb); - break; - case NL80211_CMD_AUTHENTICATE: - case NL80211_CMD_ASSOCIATE: - case NL80211_CMD_DEAUTHENTICATE: - case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_FRAME_TX_STATUS: - case NL80211_CMD_UNPROT_DEAUTHENTICATE: - case NL80211_CMD_UNPROT_DISASSOCIATE: - mlme_event(drv, cmd, tb[NL80211_ATTR_FRAME], - tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], - tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE], - tb[NL80211_ATTR_RX_SIGNAL_DBM]); - break; - case NL80211_CMD_CONNECT: - case NL80211_CMD_ROAM: - mlme_event_connect(drv, cmd, - tb[NL80211_ATTR_STATUS_CODE], - tb[NL80211_ATTR_MAC], - tb[NL80211_ATTR_REQ_IE], - tb[NL80211_ATTR_RESP_IE]); - break; - case NL80211_CMD_CH_SWITCH_NOTIFY: - mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ], - tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - break; - case NL80211_CMD_DISCONNECT: - mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], - tb[NL80211_ATTR_MAC], - tb[NL80211_ATTR_DISCONNECTED_BY_AP]); - break; - case NL80211_CMD_MICHAEL_MIC_FAILURE: - mlme_event_michael_mic_failure(bss, tb); - break; - case NL80211_CMD_JOIN_IBSS: - mlme_event_join_ibss(drv, tb); - break; - case NL80211_CMD_REMAIN_ON_CHANNEL: - mlme_event_remain_on_channel(drv, 0, tb); - break; - case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: - mlme_event_remain_on_channel(drv, 1, tb); - break; - case NL80211_CMD_NOTIFY_CQM: - nl80211_cqm_event(drv, tb); - break; - case NL80211_CMD_REG_CHANGE: - wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); - wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, - NULL); - break; - case NL80211_CMD_REG_BEACON_HINT: - wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); - wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, - NULL); - break; - case NL80211_CMD_NEW_STATION: - nl80211_new_station_event(drv, tb); - break; - case NL80211_CMD_DEL_STATION: - nl80211_del_station_event(drv, tb); - break; - case NL80211_CMD_SET_REKEY_OFFLOAD: - nl80211_rekey_offload_event(drv, tb); - break; - case NL80211_CMD_PMKSA_CANDIDATE: - nl80211_pmksa_candidate_event(drv, tb); - break; - case NL80211_CMD_PROBE_CLIENT: - nl80211_client_probe_event(drv, tb); - break; - case NL80211_CMD_TDLS_OPER: - nl80211_tdls_oper_event(drv, tb); - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " - "(cmd=%d)", cmd); - break; - } + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); } -static int process_drv_event(struct nl_msg *msg, void *arg) +static void nl80211_del_p2pdev(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct i802_bss *bss; - int ifidx = -1; + struct nl_msg *msg; + int ret; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE); + ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL); - if (tb[NL80211_ATTR_IFINDEX]) - ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(-ret)); +} - for (bss = &drv->first_bss; bss; bss = bss->next) { - if (ifidx == -1 || ifidx == bss->ifindex) { - do_process_drv_event(bss, gnlh->cmd, tb); - return NL_SKIP; - } - } - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign " - "interface (ifindex %d)", gnlh->cmd, ifidx); +static int nl80211_set_p2pdev(struct i802_bss *bss, int start) +{ + struct nl_msg *msg; + int ret; + + msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE : + NL80211_CMD_STOP_P2P_DEVICE); + ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL); - return NL_SKIP; + wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s", + start ? "Start" : "Stop", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(-ret)); + return ret; } -static int process_global_event(struct nl_msg *msg, void *arg) +static int i802_set_iface_flags(struct i802_bss *bss, int up) { - struct nl80211_global *global = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct wpa_driver_nl80211_data *drv, *tmp; - int ifidx = -1; - struct i802_bss *bss; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_IFINDEX]) - ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + enum nl80211_iftype nlmode; - dl_list_for_each_safe(drv, tmp, &global->interfaces, - struct wpa_driver_nl80211_data, list) { - for (bss = &drv->first_bss; bss; bss = bss->next) { - if (ifidx == -1 || ifidx == bss->ifindex) { - do_process_drv_event(bss, gnlh->cmd, tb); - return NL_SKIP; - } - } + nlmode = nl80211_get_ifmode(bss); + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + return linux_set_iface_flags(bss->drv->global->ioctl_sock, + bss->ifname, up); } - return NL_SKIP; + /* P2P Device has start/stop which is equivalent */ + return nl80211_set_p2pdev(bss, up); } -static int process_bss_event(struct nl_msg *msg, void *arg) +#ifdef CONFIG_TESTING_OPTIONS +static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg) { - struct i802_bss *bss = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + /* struct wpa_driver_nl80211_data *drv = arg; */ struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + + wpa_printf(MSG_DEBUG, + "nl80211: QCA vendor test command response received"); nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); - - switch (gnlh->cmd) { - case NL80211_CMD_FRAME: - case NL80211_CMD_FRAME_TX_STATUS: - mlme_event(bss->drv, gnlh->cmd, tb[NL80211_ATTR_FRAME], - tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], - tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE], - tb[NL80211_ATTR_RX_SIGNAL_DBM]); - break; - case NL80211_CMD_UNEXPECTED_FRAME: - nl80211_spurious_frame(bss, tb, 0); - break; - case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: - nl80211_spurious_frame(bss, tb, 1); - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " - "(cmd=%d)", gnlh->cmd); - break; + if (!tb[NL80211_ATTR_VENDOR_DATA]) { + wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute"); + return NL_SKIP; } + wpa_hexdump(MSG_DEBUG, + "nl80211: Received QCA vendor test command response", + nla_data(tb[NL80211_ATTR_VENDOR_DATA]), + nla_len(tb[NL80211_ATTR_VENDOR_DATA])); + return NL_SKIP; } +#endif /* CONFIG_TESTING_OPTIONS */ -static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, - void *handle) +static void qca_vendor_test(struct wpa_driver_nl80211_data *drv) { - struct nl_cb *cb = eloop_ctx; +#ifdef CONFIG_TESTING_OPTIONS + struct nl_msg *msg; + struct nlattr *params; + int ret; - wpa_printf(MSG_DEBUG, "nl80211: Event message available"); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_TEST) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) { + nlmsg_free(msg); + return; + } + nla_nest_end(msg, params); - nl_recvmsgs(handle, cb); + ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv); + wpa_printf(MSG_DEBUG, + "nl80211: QCA vendor test command returned %d (%s)", + ret, strerror(-ret)); +#endif /* CONFIG_TESTING_OPTIONS */ } -/** - * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain - * @priv: driver_nl80211 private data - * @alpha2_arg: country to which to switch to - * Returns: 0 on success, -1 on failure - * - * This asks nl80211 to set the regulatory domain for given - * country ISO / IEC alpha2. - */ -static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) +static int +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, + const u8 *set_addr, int first, + const char *driver_params) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - char alpha2[3]; - struct nl_msg *msg; + struct i802_bss *bss = drv->first_bss; + int send_rfkill_event = 0; + enum nl80211_iftype nlmode; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + drv->ifindex = if_nametoindex(bss->ifname); + bss->ifindex = drv->ifindex; + bss->wdev_id = drv->global->if_add_wdevid; + bss->wdev_id_set = drv->global->if_add_wdevid_set; - alpha2[0] = alpha2_arg[0]; - alpha2[1] = alpha2_arg[1]; - alpha2[2] = '\0'; + bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex; + bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set; + drv->global->if_add_wdevid_set = 0; - nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG); + if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) + bss->static_ap = 1; - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); - if (send_and_recv_msgs(drv, msg, NULL, NULL)) - return -EINVAL; - return 0; -nla_put_failure: - nlmsg_free(msg); - return -EINVAL; -} + if (wpa_driver_nl80211_capa(drv)) + return -1; + if (driver_params && nl80211_set_param(bss, driver_params) < 0) + return -1; -struct wiphy_info_data { - struct wpa_driver_capa *capa; + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + bss->ifname, drv->phyname); - unsigned int error:1; - unsigned int device_ap_sme:1; - unsigned int poll_command_supported:1; - unsigned int data_tx_status:1; - unsigned int monitor_supported:1; -}; + if (set_addr && + (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) || + linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + set_addr))) + return -1; + if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) + drv->start_mode_ap = 1; -static unsigned int probe_resp_offload_support(int supp_protocols) -{ - unsigned int prot = 0; + if (drv->hostapd || bss->static_ap) + nlmode = NL80211_IFTYPE_AP; + else if (bss->if_dynamic) + nlmode = nl80211_get_ifmode(bss); + else + nlmode = NL80211_IFTYPE_STATION; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; + if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode"); + return -1; + } - return prot; -} + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) + nl80211_get_macaddr(bss); + if (!rfkill_is_blocked(drv->rfkill)) { + int ret = i802_set_iface_flags(bss, 1); + if (ret) { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return ret; + } + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) + return ret; + } else { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", bss->ifname); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) + return 0; + drv->if_disabled = 1; + send_rfkill_event = 1; + } -static int wiphy_info_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct wiphy_info_data *info = arg; - int p2p_go_supported = 0, p2p_client_supported = 0; - int p2p_concurrent = 0, p2p_multichan_concurrent = 0; - int auth_supported = 0, connect_supported = 0; - struct wpa_driver_capa *capa = info->capa; - static struct nla_policy - iface_combination_policy[NUM_NL80211_IFACE_COMB] = { - [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, - [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, - [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, - [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, - }, - iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { - [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, - [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, - }; + if (!drv->hostapd) + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + bss->addr)) + return -1; + os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); - if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) - capa->max_scan_ssids = - nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); - - if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) - capa->max_sched_scan_ssids = - nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); - - if (tb[NL80211_ATTR_MAX_MATCH_SETS]) - capa->max_match_sets = - nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); - - if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { - struct nlattr *nl_mode; - int i; - nla_for_each_nested(nl_mode, - tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - switch (nla_type(nl_mode)) { - case NL80211_IFTYPE_AP: - capa->flags |= WPA_DRIVER_FLAGS_AP; - break; - case NL80211_IFTYPE_P2P_GO: - p2p_go_supported = 1; - break; - case NL80211_IFTYPE_P2P_CLIENT: - p2p_client_supported = 1; - break; - case NL80211_IFTYPE_MONITOR: - info->monitor_supported = 1; - break; - } - } + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); } - if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) { - struct nlattr *nl_combi; - int rem_combi; - - nla_for_each_nested(nl_combi, - tb[NL80211_ATTR_INTERFACE_COMBINATIONS], - rem_combi) { - struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; - struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; - struct nlattr *nl_limit, *nl_mode; - int err, rem_limit, rem_mode; - int combination_has_p2p = 0, combination_has_mgd = 0; - - err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, - nl_combi, - iface_combination_policy); - if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || - !tb_comb[NL80211_IFACE_COMB_MAXNUM] || - !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) - goto broken_combination; - - nla_for_each_nested(nl_limit, - tb_comb[NL80211_IFACE_COMB_LIMITS], - rem_limit) { - err = nla_parse_nested(tb_limit, - MAX_NL80211_IFACE_LIMIT, - nl_limit, - iface_limit_policy); - if (err || - !tb_limit[NL80211_IFACE_LIMIT_TYPES]) - goto broken_combination; - - nla_for_each_nested( - nl_mode, - tb_limit[NL80211_IFACE_LIMIT_TYPES], - rem_mode) { - int ift = nla_type(nl_mode); - if (ift == NL80211_IFTYPE_P2P_GO || - ift == NL80211_IFTYPE_P2P_CLIENT) - combination_has_p2p = 1; - if (ift == NL80211_IFTYPE_STATION) - combination_has_mgd = 1; - } - if (combination_has_p2p && combination_has_mgd) - break; - } + if (drv->vendor_cmd_test_avail) + qca_vendor_test(drv); - if (combination_has_p2p && combination_has_mgd) { - p2p_concurrent = 1; - if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1) - p2p_multichan_concurrent = 1; - break; - } - -broken_combination: - ; - } - } + return 0; +} - if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) { - struct nlattr *nl_cmd; - int i; - nla_for_each_nested(nl_cmd, - tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { - switch (nla_get_u32(nl_cmd)) { - case NL80211_CMD_AUTHENTICATE: - auth_supported = 1; - break; - case NL80211_CMD_CONNECT: - connect_supported = 1; - break; - case NL80211_CMD_START_SCHED_SCAN: - capa->sched_scan_supported = 1; - break; - case NL80211_CMD_PROBE_CLIENT: - info->poll_command_supported = 1; - break; - } - } - } +static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; - if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { - wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " - "off-channel TX"); - capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; - } + wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", + drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON); + return send_and_recv_msgs(drv, msg, NULL, NULL); +} - if (tb[NL80211_ATTR_ROAM_SUPPORT]) { - wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); - capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; - } - /* default to 5000 since early versions of mac80211 don't set it */ - capa->max_remain_on_chan = 5000; +/** + * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface + * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_nl80211_init(). + */ +static void wpa_driver_nl80211_deinit(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; - if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) - capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; + wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d", + bss->ifname, drv->disabled_11b_rates); - if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]) - capa->max_remain_on_chan = - nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + bss->in_deinit = 1; + if (drv->data_tx_status) + eloop_unregister_read_sock(drv->eapol_tx_sock); + if (drv->eapol_tx_sock >= 0) + close(drv->eapol_tx_sock); - if (auth_supported) - capa->flags |= WPA_DRIVER_FLAGS_SME; - else if (!connect_supported) { - wpa_printf(MSG_INFO, "nl80211: Driver does not support " - "authentication/association or connect commands"); - info->error = 1; + if (bss->nl_preq) + wpa_driver_nl80211_probe_req_report(bss, 0); + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + if (drv->rtnl_sk) + nl80211_handle_destroy(drv->rtnl_sk); } - - if (p2p_go_supported && p2p_client_supported) - capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; - if (p2p_concurrent) { - wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " - "interface (driver advertised support)"); - capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; - - if (p2p_multichan_concurrent) { - wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " - "concurrent (driver advertised support)"); - capa->flags |= - WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT; - } + if (bss->added_bridge) { + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname, + 0) < 0) + wpa_printf(MSG_INFO, + "nl80211: Could not set bridge %s down", + bss->brname); + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + bss->brname, strerror(errno)); } - if (tb[NL80211_ATTR_TDLS_SUPPORT]) { - wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); - capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + nl80211_remove_monitor_interface(drv); - if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) { - wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); - capa->flags |= - WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; - } + if (is_ap_interface(drv->nlmode)) + wpa_driver_nl80211_del_beacon(drv); + + if (drv->eapol_sock >= 0) { + eloop_unregister_read_sock(drv->eapol_sock); + close(drv->eapol_sock); } - if (tb[NL80211_ATTR_DEVICE_AP_SME]) - info->device_ap_sme = 1; + if (drv->if_indices != drv->default_if_indices) + os_free(drv->if_indices); - if (tb[NL80211_ATTR_FEATURE_FLAGS]) { - u32 flags = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]); + if (drv->disabled_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); - if (flags & NL80211_FEATURE_SK_TX_STATUS) - info->data_tx_status = 1; + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, + IF_OPER_UP); + eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx); + rfkill_deinit(drv->rfkill); - if (flags & NL80211_FEATURE_INACTIVITY_TIMER) - capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - if (flags & NL80211_FEATURE_SAE) - capa->flags |= WPA_DRIVER_FLAGS_SAE; + if (!drv->start_iface_up) + (void) i802_set_iface_flags(bss, 0); - if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) - capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; + if (drv->addr_changed) { + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, + 0) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not set interface down to restore permanent MAC address"); + } + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + drv->perm_addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore permanent MAC address"); + } } - if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) { - int protocols = - nla_get_u32(tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); - wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response " - "offload in AP mode"); - capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; - capa->probe_resp_offloads = - probe_resp_offload_support(protocols); + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (!drv->hostapd || !drv->start_mode_ap) + wpa_driver_nl80211_set_mode(bss, + NL80211_IFTYPE_STATION); + nl80211_mgmt_unsubscribe(bss, "deinit"); + } else { + nl80211_mgmt_unsubscribe(bss, "deinit"); + nl80211_del_p2pdev(bss); } - return NL_SKIP; -} - - -static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, - struct wiphy_info_data *info) -{ - struct nl_msg *msg; + nl80211_destroy_bss(drv->first_bss); - os_memset(info, 0, sizeof(*info)); - info->capa = &drv->capa; - - msg = nlmsg_alloc(); - if (!msg) - return -1; + os_free(drv->filter_ssids); - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + os_free(drv->auth_ie); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex); + if (drv->in_interface_list) + dl_list_del(&drv->list); - if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0) - return 0; - msg = NULL; -nla_put_failure: - nlmsg_free(msg); - return -1; + os_free(drv->extended_capa); + os_free(drv->extended_capa_mask); + os_free(drv->first_bss); + os_free(drv); } -static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) +static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) { - struct wiphy_info_data info; - if (wpa_driver_nl80211_get_info(drv, &info)) - return -1; - - if (info.error) - return -1; - - drv->has_capability = 1; - /* 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.auth = WPA_DRIVER_AUTH_OPEN | - WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - - drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; - drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; - drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - - if (!info.device_ap_sme) { - drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; - - /* - * No AP SME is currently assumed to also indicate no AP MLME - * in the driver/firmware. - */ - drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + return WLAN_CIPHER_SUITE_WEP40; + return WLAN_CIPHER_SUITE_WEP104; + case WPA_ALG_TKIP: + return WLAN_CIPHER_SUITE_TKIP; + case WPA_ALG_CCMP: + return WLAN_CIPHER_SUITE_CCMP; + case WPA_ALG_GCMP: + return WLAN_CIPHER_SUITE_GCMP; + case WPA_ALG_CCMP_256: + return WLAN_CIPHER_SUITE_CCMP_256; + case WPA_ALG_GCMP_256: + return WLAN_CIPHER_SUITE_GCMP_256; + case WPA_ALG_IGTK: + return WLAN_CIPHER_SUITE_AES_CMAC; + case WPA_ALG_BIP_GMAC_128: + return WLAN_CIPHER_SUITE_BIP_GMAC_128; + case WPA_ALG_BIP_GMAC_256: + return WLAN_CIPHER_SUITE_BIP_GMAC_256; + case WPA_ALG_BIP_CMAC_256: + return WLAN_CIPHER_SUITE_BIP_CMAC_256; + case WPA_ALG_SMS4: + return WLAN_CIPHER_SUITE_SMS4; + case WPA_ALG_KRK: + return WLAN_CIPHER_SUITE_KRK; + case WPA_ALG_NONE: + case WPA_ALG_PMK: + wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d", + alg); + return 0; } - drv->device_ap_sme = info.device_ap_sme; - drv->poll_command_supported = info.poll_command_supported; - drv->data_tx_status = info.data_tx_status; + wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d", + alg); + return 0; +} - /* - * If poll command and tx status are supported, mac80211 is new enough - * to have everything we need to not need monitor interfaces. - */ - drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; - if (drv->device_ap_sme && drv->use_monitor) { - /* - * Non-mac80211 drivers may not support monitor interface. - * Make sure we do not get stuck with incorrect capability here - * by explicitly testing this. - */ - if (!info.monitor_supported) { - wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " - "with device_ap_sme since no monitor mode " - "support detected"); - drv->use_monitor = 0; - } +static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + return WLAN_CIPHER_SUITE_CCMP_256; + case WPA_CIPHER_GCMP_256: + return WLAN_CIPHER_SUITE_GCMP_256; + case WPA_CIPHER_CCMP: + return WLAN_CIPHER_SUITE_CCMP; + case WPA_CIPHER_GCMP: + return WLAN_CIPHER_SUITE_GCMP; + case WPA_CIPHER_TKIP: + return WLAN_CIPHER_SUITE_TKIP; + case WPA_CIPHER_WEP104: + return WLAN_CIPHER_SUITE_WEP104; + case WPA_CIPHER_WEP40: + return WLAN_CIPHER_SUITE_WEP40; + case WPA_CIPHER_GTK_NOT_USED: + return WLAN_CIPHER_SUITE_NO_GROUP_ADDR; } - /* - * If we aren't going to use monitor interfaces, but the - * driver doesn't support data TX status, we won't get TX - * status for EAPOL frames. - */ - if (!drv->use_monitor && !info.data_tx_status) - drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - return 0; } -#ifdef ANDROID -static int android_genl_ctrl_resolve(struct nl_handle *handle, - const char *name) +static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], + int max_suites) { - /* - * Android ICS has very minimal genl_ctrl_resolve() implementation, so - * need to work around that. - */ - struct nl_cache *cache = NULL; - struct genl_family *nl80211 = NULL; - int id = -1; - - if (genl_ctrl_alloc_cache(handle, &cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto fail; - } + int num_suites = 0; - nl80211 = genl_ctrl_search_by_name(cache, name); - if (nl80211 == NULL) - goto fail; - - id = genl_family_get_id(nl80211); - -fail: - if (nl80211) - genl_family_put(nl80211); - if (cache) - nl_cache_free(cache); + if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256; + if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256; + if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP) + suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; - return id; + return num_suites; } -#define genl_ctrl_resolve android_genl_ctrl_resolve -#endif /* ANDROID */ -static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) +static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, + const u8 *key, size_t key_len) { + struct nl_msg *msg; int ret; - global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (global->nl_cb == NULL) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " - "callbacks"); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) + return 0; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) || + nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) { + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); return -1; } + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Key management set key failed: ret=%d (%s)", + ret, strerror(-ret)); + } - global->nl = nl_create_handle(global->nl_cb, "nl"); - if (global->nl == NULL) - goto err; + return ret; +} - global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211"); - if (global->nl80211_id < 0) { - wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " - "found"); - goto err; - } - global->nl_event = nl_create_handle(global->nl_cb, "event"); - if (global->nl_event == NULL) - goto err; +static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, + 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_nl80211_data *drv = bss->drv; + int ifindex; + struct nl_msg *msg; + int ret; + int tdls = 0; - ret = nl_get_multicast_id(global, "nl80211", "scan"); - if (ret >= 0) - ret = nl_socket_add_membership(global->nl_event, ret); - if (ret < 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " - "membership for scan events: %d (%s)", - ret, strerror(-ret)); - goto err; - } + /* Ignore for P2P Device */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + return 0; - ret = nl_get_multicast_id(global, "nl80211", "mlme"); - if (ret >= 0) - ret = nl_socket_add_membership(global->nl_event, ret); - if (ret < 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " - "membership for mlme events: %d (%s)", - ret, strerror(-ret)); - goto err; + ifindex = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " + "set_tx=%d seq_len=%lu key_len=%lu", + __func__, ifindex, ifname, alg, addr, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); +#ifdef CONFIG_TDLS + if (key_idx == -1) { + key_idx = 0; + tdls = 1; } +#endif /* CONFIG_TDLS */ - ret = nl_get_multicast_id(global, "nl80211", "regulatory"); - if (ret >= 0) - ret = nl_socket_add_membership(global->nl_event, ret); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " - "membership for regulatory events: %d (%s)", - ret, strerror(-ret)); - /* Continue without regulatory events */ + if (alg == WPA_ALG_PMK && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { + wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key", + __func__); + ret = issue_key_mgmt_set_key(drv, key, key_len); + return ret; } - nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, - no_seq_check, NULL); - nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, - process_global_event, global); + if (alg == WPA_ALG_NONE) { + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); + if (!msg) + return -ENOBUFS; + } else { + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY); + if (!msg || + nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) || + nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len))) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len); + } - eloop_register_read_sock(nl_socket_get_fd(global->nl_event), - wpa_driver_nl80211_event_receive, - global->nl_cb, global->nl_event); + if (seq && seq_len) { + if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq)) + goto fail; + wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len); + } - return 0; + if (addr && !is_broadcast_ether_addr(addr)) { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + goto fail; -err: - nl_destroy_handles(&global->nl_event); - nl_destroy_handles(&global->nl); - nl_cb_put(global->nl_cb); - global->nl_cb = NULL; - return -1; -} + if (alg != WPA_ALG_WEP && key_idx && !set_tx) { + wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); + if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, + NL80211_KEYTYPE_GROUP)) + goto fail; + } + } else if (addr && is_broadcast_ether_addr(addr)) { + struct nlattr *types; + wpa_printf(MSG_DEBUG, " broadcast key"); -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) -{ - drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!drv->nl_cb) { - wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct"); - return -1; + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); + if (!types || + nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST)) + goto fail; + nla_nest_end(msg, types); } + if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx)) + goto fail; - nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, - no_seq_check, NULL); - nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, - process_drv_event, drv); - - return 0; -} - + ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL); + if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) + ret = 0; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)", + ret, strerror(-ret)); -static void wpa_driver_nl80211_rfkill_blocked(void *ctx) -{ - wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); /* - * This may be for any interface; use ifdown event to disable - * interface. + * If we failed or don't need to set the default TX key (below), + * we're done here. */ -} + if (ret || !set_tx || alg == WPA_ALG_NONE || tdls) + return ret; + if (is_ap_interface(drv->nlmode) && addr && + !is_broadcast_ether_addr(addr)) + return ret; + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) || + nla_put_flag(msg, (alg == WPA_ALG_IGTK || + alg == WPA_ALG_BIP_GMAC_128 || + alg == WPA_ALG_BIP_GMAC_256 || + alg == WPA_ALG_BIP_CMAC_256) ? + NL80211_ATTR_KEY_DEFAULT_MGMT : + NL80211_ATTR_KEY_DEFAULT)) + goto fail; + if (addr && is_broadcast_ether_addr(addr)) { + struct nlattr *types; -static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) -{ - struct wpa_driver_nl80211_data *drv = ctx; - wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); - if (linux_set_iface_flags(drv->global->ioctl_sock, - drv->first_bss.ifname, 1)) { - wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " - "after rfkill unblock"); - return; + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); + if (!types || + nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST)) + goto fail; + nla_nest_end(msg, types); + } else if (addr) { + struct nlattr *types; + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); + if (!types || + nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST)) + goto fail; + nla_nest_end(msg, types); } - /* rtnetlink ifup handler will report interface as enabled */ + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; " + "err=%d %s)", ret, strerror(-ret)); + return ret; + +fail: + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); + return -ENOBUFS; } -static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv) +static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, + int key_idx, int defkey, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) { - /* Find phy (radio) to which this interface belongs */ - char buf[90], *pos; - int f, rv; + struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY); + if (!key_attr) + return -1; - drv->phyname[0] = '\0'; - snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", - drv->first_bss.ifname); - f = open(buf, O_RDONLY); - if (f < 0) { - wpa_printf(MSG_DEBUG, "Could not open file %s: %s", - buf, strerror(errno)); - return; + if (defkey && alg == WPA_ALG_IGTK) { + if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT)) + return -1; + } else if (defkey) { + if (nla_put_flag(msg, NL80211_KEY_DEFAULT)) + return -1; } - rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); - close(f); - if (rv < 0) { - wpa_printf(MSG_DEBUG, "Could not read file %s: %s", - buf, strerror(errno)); - return; - } + if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) || + nla_put_u32(msg, NL80211_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len)) || + (seq && seq_len && + nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) || + nla_put(msg, NL80211_KEY_DATA, key_len, key)) + return -1; - drv->phyname[rv] = '\0'; - pos = os_strchr(drv->phyname, '\n'); - if (pos) - *pos = '\0'; - wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", - drv->first_bss.ifname, drv->phyname); + nla_nest_end(msg, key_attr); + + return 0; } -static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, - void *eloop_ctx, - void *handle) +static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, + struct nl_msg *msg) { - struct wpa_driver_nl80211_data *drv = eloop_ctx; - u8 data[2048]; - struct msghdr msg; - struct iovec entry; - u8 control[512]; - struct cmsghdr *cmsg; - int res, found_ee = 0, found_wifi = 0, acked = 0; - union wpa_event_data event; + int i, privacy = 0; + struct nlattr *nl_keys, *nl_key; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &entry; - msg.msg_iovlen = 1; - entry.iov_base = data; - entry.iov_len = sizeof(data); - msg.msg_control = &control; - msg.msg_controllen = sizeof(control); + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + privacy = 1; + break; + } + if (params->wps == WPS_MODE_PRIVACY) + privacy = 1; + if (params->pairwise_suite && + params->pairwise_suite != WPA_CIPHER_NONE) + privacy = 1; - res = recvmsg(sock, &msg, MSG_ERRQUEUE); - /* if error or not fitting 802.3 header, return */ - if (res < 14) - return; + if (!privacy) + return 0; - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) - { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_WIFI_STATUS) { - int *ack; + if (nla_put_flag(msg, NL80211_ATTR_PRIVACY)) + return -ENOBUFS; - found_wifi = 1; - ack = (void *)CMSG_DATA(cmsg); - acked = *ack; - } + nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS); + if (!nl_keys) + return -ENOBUFS; - if (cmsg->cmsg_level == SOL_PACKET && - cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { - struct sock_extended_err *err = - (struct sock_extended_err *)CMSG_DATA(cmsg); + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; - if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) - found_ee = 1; - } - } + nl_key = nla_nest_start(msg, i); + if (!nl_key || + nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i], + params->wep_key[i]) || + nla_put_u32(msg, NL80211_KEY_CIPHER, + params->wep_key_len[i] == 5 ? + WLAN_CIPHER_SUITE_WEP40 : + WLAN_CIPHER_SUITE_WEP104) || + nla_put_u8(msg, NL80211_KEY_IDX, i) || + (i == params->wep_tx_keyidx && + nla_put_flag(msg, NL80211_KEY_DEFAULT))) + return -ENOBUFS; - if (!found_ee || !found_wifi) - return; + nla_nest_end(msg, nl_key); + } + nla_nest_end(msg, nl_keys); - memset(&event, 0, sizeof(event)); - event.eapol_tx_status.dst = data; - event.eapol_tx_status.data = data + 14; - event.eapol_tx_status.data_len = res - 14; - event.eapol_tx_status.ack = acked; - wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); + return 0; } -static int nl80211_init_bss(struct i802_bss *bss) +int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, u16 reason_code, + int local_state_change) { - bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!bss->nl_cb) - return -1; + int ret; + struct nl_msg *msg; - nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, - no_seq_check, NULL); - nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, - process_bss_event, bss); + if (!(msg = nl80211_drv_msg(drv, 0, cmd)) || + nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) || + (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || + (local_state_change && + nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) { + nlmsg_free(msg); + return -1; + } - return 0; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed: reason=%u ret=%d (%s)", + reason_code, ret, strerror(-ret)); + } + return ret; } -static void nl80211_destroy_bss(struct i802_bss *bss) +static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, + int reason_code) { - nl_cb_put(bss->nl_cb); - bss->nl_cb = NULL; -} + int ret; + wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); + nl80211_mark_disconnected(drv); + /* Disconnect command doesn't need BSSID - it uses cached value */ + ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, + reason_code, 0); + /* + * For locally generated disconnect, supplicant already generates a + * DEAUTH event, so ignore the event from NL80211. + */ + drv->ignore_next_local_disconnect = ret == 0; -/** - * wpa_driver_nl80211_init - Initialize nl80211 driver interface - * @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 - */ -static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, - void *global_priv) -{ - struct wpa_driver_nl80211_data *drv; - struct rfkill_config *rcfg; - struct i802_bss *bss; + return ret; +} - if (global_priv == NULL) - return NULL; - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) - return NULL; - drv->global = global_priv; - drv->ctx = ctx; - bss = &drv->first_bss; - bss->drv = drv; - bss->ctx = ctx; - os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); - drv->monitor_ifidx = -1; - drv->monitor_sock = -1; - drv->eapol_tx_sock = -1; - drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; +static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, + const u8 *addr, int reason_code) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; - if (wpa_driver_nl80211_init_nl(drv)) { - os_free(drv); - return NULL; + if (drv->nlmode == NL80211_IFTYPE_ADHOC) { + nl80211_mark_disconnected(drv); + return nl80211_leave_ibss(drv, 1); } + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return wpa_driver_nl80211_disconnect(drv, reason_code); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); + nl80211_mark_disconnected(drv); + ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, + reason_code, 0); + /* + * For locally generated deauthenticate, supplicant already generates a + * DEAUTH event, so ignore the event from NL80211. + */ + drv->ignore_next_local_deauth = ret == 0; + return ret; +} - if (nl80211_init_bss(bss)) - goto failed; - nl80211_get_phy_name(drv); +static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_auth_params *params) +{ + int i; - rcfg = os_zalloc(sizeof(*rcfg)); - if (rcfg == NULL) - goto failed; - rcfg->ctx = drv; - os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); - rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; - rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; - drv->rfkill = rfkill_init(rcfg); - if (drv->rfkill == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); - os_free(rcfg); - } + drv->auth_freq = params->freq; + drv->auth_alg = params->auth_alg; + drv->auth_wep_tx_keyidx = params->wep_tx_keyidx; + drv->auth_local_state_change = params->local_state_change; + drv->auth_p2p = params->p2p; - if (wpa_driver_nl80211_finish_drv_init(drv)) - goto failed; + if (params->bssid) + os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_bssid_, 0, ETH_ALEN); - drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); - if (drv->eapol_tx_sock < 0) - goto failed; + if (params->ssid) { + os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len); + drv->auth_ssid_len = params->ssid_len; + } else + drv->auth_ssid_len = 0; - if (drv->data_tx_status) { - int enabled = 1; - if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, - &enabled, sizeof(enabled)) < 0) { - wpa_printf(MSG_DEBUG, - "nl80211: wifi status sockopt failed\n"); - drv->data_tx_status = 0; - if (!drv->use_monitor) - drv->capa.flags &= - ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - } else { - eloop_register_read_sock(drv->eapol_tx_sock, - wpa_driver_nl80211_handle_eapol_tx_status, - drv, NULL); + os_free(drv->auth_ie); + drv->auth_ie = NULL; + drv->auth_ie_len = 0; + if (params->ie) { + drv->auth_ie = os_malloc(params->ie_len); + if (drv->auth_ie) { + os_memcpy(drv->auth_ie, params->ie, params->ie_len); + drv->auth_ie_len = params->ie_len; } } - if (drv->global) { - dl_list_add(&drv->global->interfaces, &drv->list); - drv->in_interface_list = 1; - } - - return bss; - -failed: - wpa_driver_nl80211_deinit(bss); - return NULL; -} - - -static int nl80211_register_frame(struct i802_bss *bss, - struct nl_handle *nl_handle, - u16 type, const u8 *match, size_t match_len) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p", - type, nl_handle); - wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", - match, match_len); - - nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); - NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); - - ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Register frame command " - "failed (type=%u): ret=%d (%s)", - type, ret, strerror(-ret)); - wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", - match, match_len); - goto nla_put_failure; - } - ret = 0; -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - - if (bss->nl_mgmt) { - wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " - "already on! (nl_mgmt=%p)", bss->nl_mgmt); - return -1; - } - - bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt"); - if (bss->nl_mgmt == NULL) - return -1; - - eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt), - wpa_driver_nl80211_event_receive, bss->nl_cb, - bss->nl_mgmt); - - return 0; -} - - -static int nl80211_register_action_frame(struct i802_bss *bss, - const u8 *match, size_t match_len) -{ - u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); - return nl80211_register_frame(bss, bss->nl_mgmt, - type, match, match_len); -} - - -static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - - if (nl80211_alloc_mgmt_handle(bss)) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " - "handle %p", bss->nl_mgmt); - -#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) - /* GAS Initial Request */ - if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) - return -1; - /* GAS Initial Response */ - if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) - return -1; - /* GAS Comeback Request */ - if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) - return -1; - /* GAS Comeback Response */ - if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) - return -1; -#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ -#ifdef CONFIG_P2P - /* P2P Public Action */ - if (nl80211_register_action_frame(bss, - (u8 *) "\x04\x09\x50\x6f\x9a\x09", - 6) < 0) - return -1; - /* P2P Action */ - if (nl80211_register_action_frame(bss, - (u8 *) "\x7f\x50\x6f\x9a\x09", - 5) < 0) - return -1; -#endif /* CONFIG_P2P */ -#ifdef CONFIG_IEEE80211W - /* SA Query Response */ - if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) - return -1; -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_TDLS - if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { - /* TDLS Discovery Response */ - if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) < - 0) - return -1; - } -#endif /* CONFIG_TDLS */ - - /* FT Action frames */ - if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) - return -1; - else - drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | - WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; - - /* WNM - BSS Transition Management Request */ - if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) - return -1; - /* WNM-Sleep Mode Response */ - if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) - return -1; - - return 0; -} - - -static int nl80211_register_spurious_class3(struct i802_bss *bss) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - - ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " - "failed: ret=%d (%s)", - ret, strerror(-ret)); - goto nla_put_failure; - } - ret = 0; -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) -{ - static const int stypes[] = { - WLAN_FC_STYPE_AUTH, - WLAN_FC_STYPE_ASSOC_REQ, - WLAN_FC_STYPE_REASSOC_REQ, - WLAN_FC_STYPE_DISASSOC, - WLAN_FC_STYPE_DEAUTH, - WLAN_FC_STYPE_ACTION, - WLAN_FC_STYPE_PROBE_REQ, -/* Beacon doesn't work as mac80211 doesn't currently allow - * it, but it wouldn't really be the right thing anyway as - * it isn't per interface ... maybe just dump the scan - * results periodically for OLBC? - */ -// WLAN_FC_STYPE_BEACON, - }; - unsigned int i; - - if (nl80211_alloc_mgmt_handle(bss)) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " - "handle %p", bss->nl_mgmt); - - for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) { - if (nl80211_register_frame(bss, bss->nl_mgmt, - (WLAN_FC_TYPE_MGMT << 2) | - (stypes[i] << 4), - NULL, 0) < 0) { - goto out_err; - } - } - - if (nl80211_register_spurious_class3(bss)) - goto out_err; - - if (nl80211_get_wiphy_data_ap(bss) == NULL) - goto out_err; - - return 0; - -out_err: - eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt)); - nl_destroy_handles(&bss->nl_mgmt); - return -1; -} - - -static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) -{ - if (nl80211_alloc_mgmt_handle(bss)) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " - "handle %p (device SME)", bss->nl_mgmt); - - if (nl80211_register_frame(bss, bss->nl_mgmt, - (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_ACTION << 4), - NULL, 0) < 0) - goto out_err; - - return 0; - -out_err: - eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt)); - nl_destroy_handles(&bss->nl_mgmt); - return -1; -} - - -static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) -{ - if (bss->nl_mgmt == NULL) - return; - wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " - "(%s)", bss->nl_mgmt, reason); - eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt)); - nl_destroy_handles(&bss->nl_mgmt); - - nl80211_put_wiphy_data_ap(bss); -} - - -static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) -{ - wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); -} - - -static int -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) -{ - struct i802_bss *bss = &drv->first_bss; - int send_rfkill_event = 0; - - drv->ifindex = if_nametoindex(bss->ifname); - drv->first_bss.ifindex = drv->ifindex; - -#ifndef HOSTAPD - /* - * Make sure the interface starts up in station mode unless this is a - * dynamically added interface (e.g., P2P) that was already configured - * with proper iftype. - */ - if (drv->ifindex != drv->global->if_add_ifindex && - wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to " - "use managed mode"); - return -1; - } - - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { - if (rfkill_is_blocked(drv->rfkill)) { - wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " - "interface '%s' due to rfkill", - bss->ifname); - drv->if_disabled = 1; - send_rfkill_event = 1; - } else { - wpa_printf(MSG_ERROR, "nl80211: Could not set " - "interface '%s' UP", bss->ifname); - return -1; - } - } - - netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, - 1, IF_OPER_DORMANT); -#endif /* HOSTAPD */ - - if (wpa_driver_nl80211_capa(drv)) - return -1; - - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - bss->addr)) - return -1; - - if (send_rfkill_event) { - eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, - drv, drv->ctx); + for (i = 0; i < 4; i++) { + if (params->wep_key[i] && params->wep_key_len[i] && + params->wep_key_len[i] <= 16) { + os_memcpy(drv->auth_wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + drv->auth_wep_key_len[i] = params->wep_key_len[i]; + } else + drv->auth_wep_key_len[i] = 0; } - - return 0; } -static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) +static void nl80211_unmask_11b_rates(struct i802_bss *bss) { - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + struct wpa_driver_nl80211_data *drv = bss->drv; - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates) + return; - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + /* + * Looks like we failed to unmask 11b rates previously. This could + * happen, e.g., if the interface was down at the point in time when a + * P2P group was terminated. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them", + bss->ifname); + nl80211_disable_11b_rates(drv, drv->ifindex, 0); } -/** - * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface - * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() - * - * Shut down driver interface and processing of driver events. Free - * private data buffer if one was allocated in wpa_driver_nl80211_init(). - */ -static void wpa_driver_nl80211_deinit(void *priv) +static int wpa_driver_nl80211_authenticate( + struct i802_bss *bss, struct wpa_driver_auth_params *params) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, i; + struct nl_msg *msg; + enum nl80211_auth_type type; + enum nl80211_iftype nlmode; + int count = 0; + int is_retry; - bss->in_deinit = 1; - if (drv->data_tx_status) - eloop_unregister_read_sock(drv->eapol_tx_sock); - if (drv->eapol_tx_sock >= 0) - close(drv->eapol_tx_sock); - - if (bss->nl_preq) - wpa_driver_nl80211_probe_req_report(bss, 0); - if (bss->added_if_into_bridge) { - if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, - bss->ifname) < 0) - wpa_printf(MSG_INFO, "nl80211: Failed to remove " - "interface %s from bridge %s: %s", - bss->ifname, bss->brname, strerror(errno)); - } - if (bss->added_bridge) { - if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) - wpa_printf(MSG_INFO, "nl80211: Failed to remove " - "bridge %s: %s", - bss->brname, strerror(errno)); - } - - nl80211_remove_monitor_interface(drv); - - if (is_ap_interface(drv->nlmode)) - wpa_driver_nl80211_del_beacon(drv); - -#ifdef HOSTAPD - if (drv->last_freq_ht) { - /* Clear HT flags from the driver */ - struct hostapd_freq_params freq; - os_memset(&freq, 0, sizeof(freq)); - freq.freq = drv->last_freq; - i802_set_freq(priv, &freq); - } - - if (drv->eapol_sock >= 0) { - eloop_unregister_read_sock(drv->eapol_sock); - close(drv->eapol_sock); - } - - if (drv->if_indices != drv->default_if_indices) - os_free(drv->if_indices); -#endif /* HOSTAPD */ - - if (drv->disabled_11b_rates) - nl80211_disable_11b_rates(drv, drv->ifindex, 0); - - netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, - IF_OPER_UP); - rfkill_deinit(drv->rfkill); - - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - - (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); - wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION); - nl80211_mgmt_unsubscribe(bss, "deinit"); - - nl_cb_put(drv->nl_cb); - - nl80211_destroy_bss(&drv->first_bss); - - os_free(drv->filter_ssids); - - os_free(drv->auth_ie); - - if (drv->in_interface_list) - dl_list_del(&drv->list); - - os_free(drv); -} - + nl80211_unmask_11b_rates(bss); -/** - * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion - * @eloop_ctx: Driver private data - * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() - * - * This function can be used as registered timeout when starting a scan to - * generate a scan completed event if the driver does not report this. - */ -static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { - wpa_driver_nl80211_set_mode(&drv->first_bss, - drv->ap_scan_as_station); - drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - } - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} + is_retry = drv->retry_auth; + drv->retry_auth = 0; + drv->ignore_deauth_event = 0; + nl80211_mark_disconnected(drv); + os_memset(drv->auth_bssid, 0, ETH_ALEN); + if (params->bssid) + os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); + /* FIX: IBSS mode */ + nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + if (drv->nlmode != nlmode && + wpa_driver_nl80211_set_mode(bss, nlmode) < 0) + return -1; -static struct nl_msg * -nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, - struct wpa_driver_scan_params *params) -{ - struct nl_msg *msg; - int err; - size_t i; +retry: + wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", + drv->ifindex); - msg = nlmsg_alloc(); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE); if (!msg) - return NULL; - - nl80211_cmd(drv, msg, 0, cmd); - - if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) < 0) goto fail; - if (params->num_ssids) { - struct nl_msg *ssids = nlmsg_alloc(); - if (ssids == NULL) - goto fail; - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - if (nla_put(ssids, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid) < 0) { - nlmsg_free(ssids); - goto fail; - } - } - err = nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); - nlmsg_free(ssids); - if (err < 0) + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + wpa_driver_nl80211_set_key(bss->ifname, bss, WPA_ALG_WEP, + NULL, i, + i == params->wep_tx_keyidx, NULL, 0, + params->wep_key[i], + params->wep_key_len[i]); + if (params->wep_tx_keyidx != i) + continue; + if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0, + params->wep_key[i], params->wep_key_len[i])) goto fail; } - if (params->extra_ies) { - wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", - params->extra_ies, params->extra_ies_len); - if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies) < 0) + if (params->bssid) { + wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, + MAC2STR(params->bssid)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) goto fail; } - - if (params->freqs) { - struct nl_msg *freqs = nlmsg_alloc(); - if (freqs == NULL) + if (params->freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq)) goto fail; - for (i = 0; params->freqs[i]; i++) { - wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " - "MHz", params->freqs[i]); - if (nla_put_u32(freqs, i + 1, params->freqs[i]) < 0) { - nlmsg_free(freqs); - goto fail; - } - } - err = nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, - freqs); - nlmsg_free(freqs); - if (err < 0) + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid)) goto fail; } - - os_free(drv->filter_ssids); - drv->filter_ssids = params->filter_ssids; - params->filter_ssids = NULL; - drv->num_filter_ssids = params->num_filter_ssids; - - return msg; - -fail: - nlmsg_free(msg); - return NULL; -} - - -/** - * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1, timeout; - struct nl_msg *msg, *rates = NULL; - - drv->scan_for_auth = 0; - - msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params); - if (!msg) - return -1; - - if (params->p2p_probe) { - wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); - - rates = nlmsg_alloc(); - if (rates == NULL) - goto nla_put_failure; - - /* - * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates - * by masking out everything else apart from the OFDM rates 6, - * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz - * rates are left enabled. - */ - NLA_PUT(rates, NL80211_BAND_2GHZ, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); - if (nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates) < - 0) - goto nla_put_failure; - - NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); + if (params->ie && + nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie)) + goto fail; + if (params->sae_data) { + wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, + params->sae_data_len); + if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, + params->sae_data)) + goto fail; + } + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) + type = NL80211_AUTHTYPE_SHARED_KEY; + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) + type = NL80211_AUTHTYPE_NETWORK_EAP; + else if (params->auth_alg & WPA_AUTH_ALG_FT) + type = NL80211_AUTHTYPE_FT; + else if (params->auth_alg & WPA_AUTH_ALG_SAE) + type = NL80211_AUTHTYPE_SAE; + else + goto fail; + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + goto fail; + if (params->local_state_change) { + wpa_printf(MSG_DEBUG, " * Local state change only"); + if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE)) + goto fail; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " - "(%s)", ret, strerror(-ret)); -#ifdef HOSTAPD - if (is_ap_interface(drv->nlmode)) { + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (auth): ret=%d (%s)", + ret, strerror(-ret)); + count++; + if (ret == -EALREADY && count == 1 && params->bssid && + !params->local_state_change) { + /* + * mac80211 does not currently accept new + * authentication if we are already authenticated. As a + * workaround, force deauthentication and try again. + */ + wpa_printf(MSG_DEBUG, "nl80211: Retry authentication " + "after forced deauthentication"); + drv->ignore_deauth_event = 1; + wpa_driver_nl80211_deauthenticate( + bss, params->bssid, + WLAN_REASON_PREV_AUTH_NOT_VALID); + nlmsg_free(msg); + goto retry; + } + + if (ret == -ENOENT && params->freq && !is_retry) { + /* + * cfg80211 has likely expired the BSS entry even + * though it was previously available in our internal + * BSS table. To recover quickly, start a single + * channel scan on the specified channel. + */ + struct wpa_driver_scan_params scan; + int freqs[2]; + + os_memset(&scan, 0, sizeof(scan)); + scan.num_ssids = 1; + if (params->ssid) { + scan.ssids[0].ssid = params->ssid; + scan.ssids[0].ssid_len = params->ssid_len; + } + freqs[0] = params->freq; + freqs[1] = 0; + scan.freqs = freqs; + wpa_printf(MSG_DEBUG, "nl80211: Trigger single " + "channel scan to refresh cfg80211 BSS " + "entry"); + ret = wpa_driver_nl80211_scan(bss, &scan); + if (ret == 0) { + nl80211_copy_auth_params(drv, params); + drv->scan_for_auth = 1; + } + } else if (is_retry) { /* - * mac80211 does not allow scan requests in AP mode, so - * try to do this in station mode. + * Need to indicate this with an event since the return + * value from the retry is not delivered to core code. */ - if (wpa_driver_nl80211_set_mode( - bss, NL80211_IFTYPE_STATION)) - goto nla_put_failure; - - if (wpa_driver_nl80211_scan(drv, params)) { - wpa_driver_nl80211_set_mode(bss, drv->nlmode); - goto nla_put_failure; - } - - /* Restore AP mode when processing scan results */ - drv->ap_scan_as_station = drv->nlmode; - ret = 0; - } else - goto nla_put_failure; -#else /* HOSTAPD */ - goto nla_put_failure; -#endif /* HOSTAPD */ - } - - /* Not all drivers generate "scan completed" wireless event, so try to - * read results after a timeout. */ - timeout = 10; - if (drv->scan_complete_events) { - /* - * The driver seems to deliver events to notify when scan is - * complete, so use longer timeout to avoid race conditions - * with scanning and following association request. - */ - timeout = 30; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "nl80211: Authentication retry " + "failed"); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, drv->auth_bssid_, + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, + &event); + } + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Authentication request send successfully"); } - wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " - "seconds", ret, timeout); - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); -nla_put_failure: +fail: nlmsg_free(msg); - nlmsg_free(rates); return ret; } -/** - * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds - * Returns: 0 on success, -1 on failure or if not supported - */ -static int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval) +int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - struct nl_msg *msg; - struct nl_msg *match_set_ssid = NULL, *match_sets = NULL; - struct nl_msg *match_set_rssi = NULL; - size_t i; + struct wpa_driver_auth_params params; + struct i802_bss *bss = drv->first_bss; + int i; -#ifdef ANDROID - if (!drv->capa.sched_scan_supported) - return android_pno_start(bss, params); -#endif /* ANDROID */ + wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again"); - msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params); - if (!msg) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); - - if ((drv->num_filter_ssids && - (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || - params->filter_rssi) { - match_sets = nlmsg_alloc(); - if (match_sets == NULL) - goto nla_put_failure; - - for (i = 0; i < drv->num_filter_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, - "nl80211: Sched scan filter SSID", - drv->filter_ssids[i].ssid, - drv->filter_ssids[i].ssid_len); - - match_set_ssid = nlmsg_alloc(); - if (match_set_ssid == NULL) - goto nla_put_failure; - NLA_PUT(match_set_ssid, - NL80211_ATTR_SCHED_SCAN_MATCH_SSID, - drv->filter_ssids[i].ssid_len, - drv->filter_ssids[i].ssid); - - if (nla_put_nested(match_sets, i + 1, match_set_ssid) < - 0) - goto nla_put_failure; - } + os_memset(¶ms, 0, sizeof(params)); + params.freq = drv->auth_freq; + params.auth_alg = drv->auth_alg; + params.wep_tx_keyidx = drv->auth_wep_tx_keyidx; + params.local_state_change = drv->auth_local_state_change; + params.p2p = drv->auth_p2p; - if (params->filter_rssi) { - match_set_rssi = nlmsg_alloc(); - if (match_set_rssi == NULL) - goto nla_put_failure; - NLA_PUT_U32(match_set_rssi, - NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, - params->filter_rssi); - wpa_printf(MSG_MSGDUMP, - "nl80211: Sched scan RSSI filter %d dBm", - params->filter_rssi); - if (nla_put_nested(match_sets, 0, match_set_rssi) < 0) - goto nla_put_failure; - } + if (!is_zero_ether_addr(drv->auth_bssid_)) + params.bssid = drv->auth_bssid_; - if (nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH, - match_sets) < 0) - goto nla_put_failure; + if (drv->auth_ssid_len) { + params.ssid = drv->auth_ssid; + params.ssid_len = drv->auth_ssid_len; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - - /* TODO: if we get an error here, we should fall back to normal scan */ + params.ie = drv->auth_ie; + params.ie_len = drv->auth_ie_len; - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " - "ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; + for (i = 0; i < 4; i++) { + if (drv->auth_wep_key_len[i]) { + params.wep_key[i] = drv->auth_wep_key[i]; + params.wep_key_len[i] = drv->auth_wep_key_len[i]; + } } - wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " - "scan interval %d msec", ret, interval); - -nla_put_failure: - nlmsg_free(match_set_ssid); - nlmsg_free(match_sets); - nlmsg_free(match_set_rssi); - nlmsg_free(msg); - return ret; + drv->retry_auth = 1; + return wpa_driver_nl80211_authenticate(bss, ¶ms); } -/** - * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * Returns: 0 on success, -1 on failure or if not supported - */ -static int wpa_driver_nl80211_stop_sched_scan(void *priv) +static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, + const void *data, size_t len, + int encrypt, int noack, + unsigned int freq, int no_cck, + int offchanok, unsigned int wait_time) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0; - struct nl_msg *msg; - -#ifdef ANDROID - if (!drv->capa.sched_scan_supported) - return android_pno_stop(bss); -#endif /* ANDROID */ - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + u64 cookie; + int res; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: " - "ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; + if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) { + freq = nl80211_get_assoc_freq(drv); + wpa_printf(MSG_DEBUG, + "nl80211: send_frame - Use assoc_freq=%u for IBSS", + freq); } - - wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret); - -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) -{ - const u8 *end, *pos; - - if (ies == NULL) - return NULL; - - pos = ies; - end = ies + ies_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u", + bss->freq); + freq = bss->freq; } - return NULL; -} - - -static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, - const u8 *ie, size_t ie_len) -{ - const u8 *ssid; - size_t i; - - if (drv->filter_ssids == NULL) - return 0; + if (drv->use_monitor) { + wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor", + freq, bss->freq); + return nl80211_send_monitor(drv, data, len, encrypt, noack); + } - ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); - if (ssid == NULL) - return 1; + wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); + res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, + &cookie, no_cck, noack, offchanok); + if (res == 0 && !noack) { + const struct ieee80211_mgmt *mgmt; + u16 fc; - for (i = 0; i < drv->num_filter_ssids; i++) { - if (ssid[1] == drv->filter_ssids[i].ssid_len && - os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == - 0) - return 0; + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Update send_action_cookie from 0x%llx to 0x%llx", + (long long unsigned int) + drv->send_action_cookie, + (long long unsigned int) cookie); + drv->send_action_cookie = cookie; + } } - return 1; + return res; } -static int bss_info_handler(struct nl_msg *msg, void *arg) +static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, + size_t data_len, int noack, + unsigned int freq, int no_cck, + int offchanok, + unsigned int wait_time) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *bss[NL80211_BSS_MAX + 1]; - static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { - [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, - [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, - [NL80211_BSS_TSF] = { .type = NLA_U64 }, - [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, - [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, - [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, - [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, - [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, - [NL80211_BSS_STATUS] = { .type = NLA_U32 }, - [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, - [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, - }; - struct nl80211_bss_info_arg *_arg = arg; - struct wpa_scan_results *res = _arg->res; - struct wpa_scan_res **tmp; - struct wpa_scan_res *r; - const u8 *ie, *beacon_ie; - size_t ie_len, beacon_ie_len; - u8 *pos; - size_t i; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (!tb[NL80211_ATTR_BSS]) - return NL_SKIP; - if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], - bss_policy)) - return NL_SKIP; - if (bss[NL80211_BSS_STATUS]) { - enum nl80211_bss_status status; - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->assoc_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", - _arg->assoc_freq); - } - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_BSSID]) { - os_memcpy(_arg->assoc_bssid, - nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - wpa_printf(MSG_DEBUG, "nl80211: Associated with " - MACSTR, MAC2STR(_arg->assoc_bssid)); - } - } - if (!res) - return NL_SKIP; - if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { - ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); - ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); - } else { - ie = NULL; - ie_len = 0; - } - if (bss[NL80211_BSS_BEACON_IES]) { - beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); - beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); - } else { - beacon_ie = NULL; - beacon_ie_len = 0; - } + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_mgmt *mgmt; + int encrypt = 1; + u16 fc; - if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, - ie ? ie_len : beacon_ie_len)) - return NL_SKIP; + mgmt = (struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da= " MACSTR + " noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x (%s) nlmode=%d", + MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time, + fc, fc2str(fc), drv->nlmode); - r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); - if (r == NULL) - return NL_SKIP; - if (bss[NL80211_BSS_BSSID]) - os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), - ETH_ALEN); - if (bss[NL80211_BSS_FREQUENCY]) - r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - if (bss[NL80211_BSS_BEACON_INTERVAL]) - r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); - if (bss[NL80211_BSS_CAPABILITY]) - r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); - r->flags |= WPA_SCAN_NOISE_INVALID; - if (bss[NL80211_BSS_SIGNAL_MBM]) { - r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); - r->level /= 100; /* mBm to dBm */ - r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; - } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { - r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - r->flags |= WPA_SCAN_QUAL_INVALID; - } else - r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; - if (bss[NL80211_BSS_TSF]) - r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); - if (bss[NL80211_BSS_SEEN_MS_AGO]) - r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); - r->ie_len = ie_len; - pos = (u8 *) (r + 1); - if (ie) { - os_memcpy(pos, ie, ie_len); - pos += ie_len; - } - r->beacon_ie_len = beacon_ie_len; - if (beacon_ie) - os_memcpy(pos, beacon_ie, beacon_ie_len); - - if (bss[NL80211_BSS_STATUS]) { - enum nl80211_bss_status status; - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - switch (status) { - case NL80211_BSS_STATUS_AUTHENTICATED: - r->flags |= WPA_SCAN_AUTHENTICATED; - break; - case NL80211_BSS_STATUS_ASSOCIATED: - r->flags |= WPA_SCAN_ASSOCIATED; - break; - default: - break; + if ((is_sta_interface(drv->nlmode) || + drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + /* + * The use of last_mgmt_freq is a bit of a hack, + * but it works due to the single-threaded nature + * of wpa_supplicant. + */ + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d", + drv->last_mgmt_freq); + freq = drv->last_mgmt_freq; } + return nl80211_send_frame_cmd(bss, freq, 0, + data, data_len, NULL, 1, noack, + 1); } - /* - * cfg80211 maintains separate BSS table entries for APs if the same - * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does - * not use frequency as a separate key in the BSS table, so filter out - * duplicated entries. Prefer associated BSS entry in such a case in - * order to get the correct frequency into the BSS table. - */ - for (i = 0; i < res->num; i++) { - const u8 *s1, *s2; - if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) - continue; - - s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), - res->res[i]->ie_len, WLAN_EID_SSID); - s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); - if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || - os_memcmp(s1, s2, 2 + s1[1]) != 0) - continue; - - /* Same BSSID,SSID was already included in scan results */ - wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " - "for " MACSTR, MAC2STR(r->bssid)); - - if ((r->flags & WPA_SCAN_ASSOCIATED) && - !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) { - os_free(res->res[i]); - res->res[i] = r; - } else - os_free(r); - return NL_SKIP; - } - - tmp = os_realloc_array(res->res, res->num + 1, - sizeof(struct wpa_scan_res *)); - if (tmp == NULL) { - os_free(r); - return NL_SKIP; + if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d", + bss->freq); + freq = bss->freq; + } + return nl80211_send_frame_cmd(bss, freq, + (int) freq == bss->freq ? 0 : + wait_time, + data, data_len, + &drv->send_action_cookie, + no_cck, noack, offchanok); } - tmp[res->num++] = r; - res->res = tmp; - - return NL_SKIP; -} - -static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, - const u8 *addr) -{ - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " - "mismatch (" MACSTR ")", MAC2STR(addr)); - wpa_driver_nl80211_mlme(drv, addr, - NL80211_CMD_DEAUTHENTICATE, - WLAN_REASON_PREV_AUTH_NOT_VALID, 1); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { + /* + * Only one of the authentication frame types is encrypted. + * In order for static WEP encryption to work properly (i.e., + * to not encrypt the frame), we need to tell mac80211 about + * the frames that must not be encrypted. + */ + u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); + if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3) + encrypt = 0; } -} - - -static void wpa_driver_nl80211_check_bss_status( - struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res) -{ - size_t i; - - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - if (r->flags & WPA_SCAN_AUTHENTICATED) { - wpa_printf(MSG_DEBUG, "nl80211: Scan results " - "indicates BSS status with " MACSTR - " as authenticated", - MAC2STR(r->bssid)); - if (is_sta_interface(drv->nlmode) && - os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && - os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID" - " in local state (auth=" MACSTR - " assoc=" MACSTR ")", - MAC2STR(drv->auth_bssid), - MAC2STR(drv->bssid)); - clear_state_mismatch(drv, r->bssid); - } - } - if (r->flags & WPA_SCAN_ASSOCIATED) { - wpa_printf(MSG_DEBUG, "nl80211: Scan results " - "indicate BSS status with " MACSTR - " as associated", - MAC2STR(r->bssid)); - if (is_sta_interface(drv->nlmode) && - !drv->associated) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(not associated) does not match " - "with BSS state"); - clear_state_mismatch(drv, r->bssid); - } else if (is_sta_interface(drv->nlmode) && - os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(associated with " MACSTR ") does " - "not match with BSS state", - MAC2STR(drv->bssid)); - clear_state_mismatch(drv, r->bssid); - clear_state_mismatch(drv, drv->bssid); - } - } - } + wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame"); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, + noack, freq, no_cck, offchanok, + wait_time); } -static struct wpa_scan_results * -nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) +static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates) { - struct nl_msg *msg; - struct wpa_scan_results *res; - int ret; - struct nl80211_bss_info_arg arg; + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return NULL; - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; + if (!basic_rates) + return 0; - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + rates[rates_len++] = basic_rates[i] / 5; - arg.drv = drv; - arg.res = res; - ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); - msg = NULL; - if (ret == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " - "BSSes)", (unsigned long) res->num); - nl80211_get_noise_for_scan_results(drv, res); - return res; - } - wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " - "(%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); - wpa_scan_results_free(res); - return NULL; + return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); } -/** - * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * Returns: Scan results on success, -1 on failure - */ -static struct wpa_scan_results * -wpa_driver_nl80211_get_scan_results(void *priv) +static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, + int slot, int ht_opmode, int ap_isolate, + const int *basic_rates) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct wpa_scan_results *res; - - res = nl80211_get_scan_results(drv); - if (res) - wpa_driver_nl80211_check_bss_status(drv, res); - return res; -} - - -static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) -{ - struct wpa_scan_results *res; - size_t i; - - res = nl80211_get_scan_results(drv); - if (res == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); - return; - } + struct nl_msg *msg; - wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s", - (int) i, (int) res->num, MAC2STR(r->bssid), - r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "", - r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) || + (cts >= 0 && + nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) || + (preamble >= 0 && + nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) || + (slot >= 0 && + nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) || + (ht_opmode >= 0 && + nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) || + (ap_isolate >= 0 && + nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) || + nl80211_put_basic_rates(msg, basic_rates)) { + nlmsg_free(msg); + return -ENOBUFS; } - wpa_scan_results_free(res); + return send_and_recv_msgs(drv, msg, NULL, NULL); } -static int wpa_driver_nl80211_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) +static int wpa_driver_nl80211_set_acl(void *priv, + struct hostapd_acl_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ifindex = if_nametoindex(ifname); struct nl_msg *msg; + struct nlattr *acl; + unsigned int i; int ret; - wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d " - "set_tx=%d seq_len=%lu key_len=%lu", - __func__, ifindex, alg, addr, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); -#ifdef CONFIG_TDLS - if (key_idx == -1) - key_idx = 0; -#endif /* CONFIG_TDLS */ + if (!(drv->capa.max_acl_mac_addrs)) + return -ENOTSUP; - msg = nlmsg_alloc(); - if (!msg) + if (params->num_mac_acl > drv->capa.max_acl_mac_addrs) + return -ENOTSUP; + + wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", + params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) || + nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? + NL80211_ACL_POLICY_DENY_UNLESS_LISTED : + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) || + (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) { + nlmsg_free(msg); return -ENOMEM; + } - if (alg == WPA_ALG_NONE) { - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY); - } else { - nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); - NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); - switch (alg) { - case WPA_ALG_WEP: - if (key_len == 5) - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP40); - else - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP104); - break; - case WPA_ALG_TKIP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_TKIP); - break; - case WPA_ALG_CCMP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_CCMP); - break; - case WPA_ALG_GCMP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_GCMP); - break; - case WPA_ALG_IGTK: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_AES_CMAC); - break; - case WPA_ALG_SMS4: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_SMS4); - break; - case WPA_ALG_KRK: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - WLAN_CIPHER_SUITE_KRK); - break; - default: - wpa_printf(MSG_ERROR, "%s: Unsupported encryption " - "algorithm %d", __func__, alg); + for (i = 0; i < params->num_mac_acl; i++) { + if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) { nlmsg_free(msg); - return -1; + return -ENOMEM; } } - if (seq && seq_len) - NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); - - if (addr && !is_broadcast_ether_addr(addr)) { - wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - if (alg != WPA_ALG_WEP && key_idx && !set_tx) { - wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); - NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, - NL80211_KEYTYPE_GROUP); - } - } else if (addr && is_broadcast_ether_addr(addr)) { - struct nl_msg *types; - int err; - wpa_printf(MSG_DEBUG, " broadcast key"); - types = nlmsg_alloc(); - if (!types) - goto nla_put_failure; - NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); - err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, - types); - nlmsg_free(types); - if (err) - goto nla_put_failure; - } - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + nla_nest_end(msg, acl); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) - ret = 0; - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)", + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)", ret, strerror(-ret)); + } - /* - * If we failed or don't need to set the default TX key (below), - * we're done here. - */ - if (ret || !set_tx || alg == WPA_ALG_NONE) - return ret; - if (is_ap_interface(drv->nlmode) && addr && - !is_broadcast_ether_addr(addr)) - return ret; + return ret; +} - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY); - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - if (alg == WPA_ALG_IGTK) - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); - else - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); - if (addr && is_broadcast_ether_addr(addr)) { - struct nl_msg *types; - int err; - types = nlmsg_alloc(); - if (!types) - goto nla_put_failure; - NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST); - err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, - types); - nlmsg_free(types); - if (err) - goto nla_put_failure; - } else if (addr) { - struct nl_msg *types; - int err; - types = nlmsg_alloc(); - if (!types) - goto nla_put_failure; - NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST); - err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES, - types); - nlmsg_free(types); - if (err) - goto nla_put_failure; +static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int) +{ + if (beacon_int > 0) { + wpa_printf(MSG_DEBUG, " * beacon_int=%d", beacon_int); + return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, + beacon_int); } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret == -ENOENT) - ret = 0; - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; " - "err=%d %s)", ret, strerror(-ret)); - return ret; - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + return 0; } -static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, - int key_idx, int defkey, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) +static int wpa_driver_nl80211_set_ap(void *priv, + struct wpa_driver_ap_params *params) { - struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY); - if (!key_attr) - return -1; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u8 cmd = NL80211_CMD_NEW_BEACON; + int ret; + int beacon_set; + int num_suites; + int smps_mode; + u32 suites[10], suite; + u32 ver; - if (defkey && alg == WPA_ALG_IGTK) - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT); - else if (defkey) - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); + beacon_set = params->reenable ? 0 : bss->beacon_set; - NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx); + wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", + beacon_set); + if (beacon_set) + cmd = NL80211_CMD_SET_BEACON; - switch (alg) { - case WPA_ALG_WEP: - if (key_len == 5) - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP40); - else - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP104); - break; - case WPA_ALG_TKIP: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP); - break; - case WPA_ALG_CCMP: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); + wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", + params->head, params->head_len); + wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail", + params->tail, params->tail_len); + wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex); + wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); + wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); + wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", + params->ssid, params->ssid_len); + if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || + nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, + params->head) || + nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, + params->tail) || + nl80211_put_beacon_int(msg, params->beacon_int) || + nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) || + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) + goto fail; + if (params->proberesp && params->proberesp_len) { + wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)", + params->proberesp, params->proberesp_len); + if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, + params->proberesp)) + goto fail; + } + switch (params->hide_ssid) { + case NO_SSID_HIDING: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use"); + if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_NOT_IN_USE)) + goto fail; break; - case WPA_ALG_GCMP: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP); + case HIDDEN_SSID_ZERO_LEN: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len"); + if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_LEN)) + goto fail; break; - case WPA_ALG_IGTK: - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_AES_CMAC); + case HIDDEN_SSID_ZERO_CONTENTS: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents"); + if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_CONTENTS)) + goto fail; break; - default: - wpa_printf(MSG_ERROR, "%s: Unsupported encryption " - "algorithm %d", __func__, alg); - return -1; + } + wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy); + if (params->privacy && + nla_put_flag(msg, NL80211_ATTR_PRIVACY)) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs); + if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == + (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { + /* Leave out the attribute */ + } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) { + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_SHARED_KEY)) + goto fail; + } else { + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_OPEN_SYSTEM)) + goto fail; } - if (seq && seq_len) - NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq); - - NLA_PUT(msg, NL80211_KEY_DATA, key_len, key); + wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version); + ver = 0; + if (params->wpa_version & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_version & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; + if (ver && + nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver)) + goto fail; - nla_nest_end(msg, key_attr); + wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x", + params->key_mgmt_suites); + num_suites = 0; + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) + suites[num_suites++] = WLAN_AKM_SUITE_8021X; + if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) + suites[num_suites++] = WLAN_AKM_SUITE_PSK; + if (num_suites && + nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32), + suites)) + goto fail; - return 0; - nla_put_failure: - return -1; -} + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA && + params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) && + nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", + params->pairwise_ciphers); + num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, + suites, ARRAY_SIZE(suites)); + if (num_suites && + nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + num_suites * sizeof(u32), suites)) + goto fail; -static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, - struct nl_msg *msg) -{ - int i, privacy = 0; - struct nlattr *nl_keys, *nl_key; + wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x", + params->group_cipher); + suite = wpa_cipher_to_cipher_suite(params->group_cipher); + if (suite && + nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite)) + goto fail; - for (i = 0; i < 4; i++) { - if (!params->wep_key[i]) - continue; - privacy = 1; + switch (params->smps_mode) { + case HT_CAP_INFO_SMPS_DYNAMIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic"); + smps_mode = NL80211_SMPS_DYNAMIC; + break; + case HT_CAP_INFO_SMPS_STATIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static"); + smps_mode = NL80211_SMPS_STATIC; + break; + default: + /* invalid - fallback to smps off */ + case HT_CAP_INFO_SMPS_DISABLED: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off"); + smps_mode = NL80211_SMPS_OFF; break; } - if (params->wps == WPS_MODE_PRIVACY) - privacy = 1; - if (params->pairwise_suite && - params->pairwise_suite != WPA_CIPHER_NONE) - privacy = 1; - - if (!privacy) - return 0; - - NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); - - nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS); - if (!nl_keys) - goto nla_put_failure; - - for (i = 0; i < 4; i++) { - if (!params->wep_key[i]) - continue; - - nl_key = nla_nest_start(msg, i); - if (!nl_key) - goto nla_put_failure; - - NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i], - params->wep_key[i]); - if (params->wep_key_len[i] == 5) - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP40); - else - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP104); - - NLA_PUT_U8(msg, NL80211_KEY_IDX, i); + if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) + goto fail; - if (i == params->wep_tx_keyidx) - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); + if (params->beacon_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", + params->beacon_ies); + if (nla_put(msg, NL80211_ATTR_IE, + wpabuf_len(params->beacon_ies), + wpabuf_head(params->beacon_ies))) + goto fail; + } + if (params->proberesp_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies", + params->proberesp_ies); + if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP, + wpabuf_len(params->proberesp_ies), + wpabuf_head(params->proberesp_ies))) + goto fail; + } + if (params->assocresp_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies", + params->assocresp_ies); + if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP, + wpabuf_len(params->assocresp_ies), + wpabuf_head(params->assocresp_ies))) + goto fail; + } - nla_nest_end(msg, nl_key); + if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { + wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d", + params->ap_max_inactivity); + if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, + params->ap_max_inactivity)) + goto fail; } - nla_nest_end(msg, nl_keys); - return 0; +#ifdef CONFIG_P2P + if (params->p2p_go_ctwindow > 0) { + if (drv->p2p_go_ctwindow_supported) { + wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d", + params->p2p_go_ctwindow); + if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW, + params->p2p_go_ctwindow)) + goto fail; + } else { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support CTWindow configuration - ignore this parameter"); + } + } +#endif /* CONFIG_P2P */ -nla_put_failure: + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", + ret, strerror(-ret)); + } else { + bss->beacon_set = 1; + nl80211_set_bss(bss, params->cts_protect, params->preamble, + params->short_slot_time, params->ht_opmode, + params->isolate, params->basic_rates); + if (beacon_set && params->freq && + params->freq->bandwidth != bss->bandwidth) { + wpa_printf(MSG_DEBUG, + "nl80211: Update BSS %s bandwidth: %d -> %d", + bss->ifname, bss->bandwidth, + params->freq->bandwidth); + ret = nl80211_set_channel(bss, params->freq, 1); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Frequency set failed: %d (%s)", + ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Frequency set succeeded for ht2040 coex"); + bss->bandwidth = params->freq->bandwidth; + } + } else if (!beacon_set) { + /* + * cfg80211 updates the driver on frequence change in AP + * mode only at the point when beaconing is started, so + * set the initial value here. + */ + bss->bandwidth = params->freq->bandwidth; + } + } + return ret; +fail: + nlmsg_free(msg); return -ENOBUFS; } -static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code, - int local_state_change) +static int nl80211_put_freq_params(struct nl_msg *msg, + const struct hostapd_freq_params *freq) { - int ret = -1; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, cmd); + wpa_printf(MSG_DEBUG, " * freq=%d", freq->freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq)) + return -ENOBUFS; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); - if (addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - if (local_state_change) - NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: MLME command failed: reason=%u ret=%d (%s)", - reason_code, ret, strerror(-ret)); - goto nla_put_failure; - } - ret = 0; + wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled); + wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled); -nla_put_failure: - nlmsg_free(msg); - return ret; -} + if (freq->vht_enabled) { + enum nl80211_chan_width cw; + wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth); + switch (freq->bandwidth) { + case 20: + cw = NL80211_CHAN_WIDTH_20; + break; + case 40: + cw = NL80211_CHAN_WIDTH_40; + break; + case 80: + if (freq->center_freq2) + cw = NL80211_CHAN_WIDTH_80P80; + else + cw = NL80211_CHAN_WIDTH_80; + break; + case 160: + cw = NL80211_CHAN_WIDTH_160; + break; + default: + return -EINVAL; + } -static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, - int reason_code) -{ - wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); - drv->associated = 0; - drv->ignore_next_local_disconnect = 0; - /* Disconnect command doesn't need BSSID - it uses cached value */ - return wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, - reason_code, 0); + wpa_printf(MSG_DEBUG, " * channel_width=%d", cw); + wpa_printf(MSG_DEBUG, " * center_freq1=%d", + freq->center_freq1); + wpa_printf(MSG_DEBUG, " * center_freq2=%d", + freq->center_freq2); + if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) || + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, + freq->center_freq1) || + (freq->center_freq2 && + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2))) + return -ENOBUFS; + } else if (freq->ht_enabled) { + enum nl80211_channel_type ct; + + wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d", + freq->sec_channel_offset); + switch (freq->sec_channel_offset) { + case -1: + ct = NL80211_CHAN_HT40MINUS; + break; + case 1: + ct = NL80211_CHAN_HT40PLUS; + break; + default: + ct = NL80211_CHAN_HT20; + break; + } + + wpa_printf(MSG_DEBUG, " * channel_type=%d", ct); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct)) + return -ENOBUFS; + } + return 0; } -static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, - int reason_code) +static int nl80211_set_channel(struct i802_bss *bss, + struct hostapd_freq_params *freq, int set_chan) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, reason_code); - wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", - __func__, MAC2STR(addr), reason_code); - drv->associated = 0; - if (drv->nlmode == NL80211_IFTYPE_ADHOC) - return nl80211_leave_ibss(drv); - return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - reason_code, 0); -} + struct nl_msg *msg; + int ret; + wpa_printf(MSG_DEBUG, + "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); -static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, - struct wpa_driver_auth_params *params) -{ - int i; + msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL : + NL80211_CMD_SET_WIPHY); + if (!msg || nl80211_put_freq_params(msg, freq) < 0) { + nlmsg_free(msg); + return -1; + } - drv->auth_freq = params->freq; - drv->auth_alg = params->auth_alg; - drv->auth_wep_tx_keyidx = params->wep_tx_keyidx; - drv->auth_local_state_change = params->local_state_change; - drv->auth_p2p = params->p2p; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) { + bss->freq = freq->freq; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " + "%d (%s)", freq->freq, ret, strerror(-ret)); + return -1; +} - if (params->bssid) - os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN); - else - os_memset(drv->auth_bssid_, 0, ETH_ALEN); - if (params->ssid) { - os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len); - drv->auth_ssid_len = params->ssid_len; - } else - drv->auth_ssid_len = 0; +static u32 sta_flags_nl80211(int flags) +{ + u32 f = 0; + if (flags & WPA_STA_AUTHORIZED) + f |= BIT(NL80211_STA_FLAG_AUTHORIZED); + if (flags & WPA_STA_WMM) + f |= BIT(NL80211_STA_FLAG_WME); + if (flags & WPA_STA_SHORT_PREAMBLE) + f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); + if (flags & WPA_STA_MFP) + f |= BIT(NL80211_STA_FLAG_MFP); + if (flags & WPA_STA_TDLS_PEER) + f |= BIT(NL80211_STA_FLAG_TDLS_PEER); + if (flags & WPA_STA_AUTHENTICATED) + f |= BIT(NL80211_STA_FLAG_AUTHENTICATED); - os_free(drv->auth_ie); - drv->auth_ie = NULL; - drv->auth_ie_len = 0; - if (params->ie) { - drv->auth_ie = os_malloc(params->ie_len); - if (drv->auth_ie) { - os_memcpy(drv->auth_ie, params->ie, params->ie_len); - drv->auth_ie_len = params->ie_len; - } - } + return f; +} - for (i = 0; i < 4; i++) { - if (params->wep_key[i] && params->wep_key_len[i] && - params->wep_key_len[i] <= 16) { - os_memcpy(drv->auth_wep_key[i], params->wep_key[i], - params->wep_key_len[i]); - drv->auth_wep_key_len[i] = params->wep_key_len[i]; - } else - drv->auth_wep_key_len[i] = 0; + +#ifdef CONFIG_MESH +static u32 sta_plink_state_nl80211(enum mesh_plink_state state) +{ + switch (state) { + case PLINK_LISTEN: + return NL80211_PLINK_LISTEN; + case PLINK_OPEN_SENT: + return NL80211_PLINK_OPN_SNT; + case PLINK_OPEN_RCVD: + return NL80211_PLINK_OPN_RCVD; + case PLINK_CNF_RCVD: + return NL80211_PLINK_CNF_RCVD; + case PLINK_ESTAB: + return NL80211_PLINK_ESTAB; + case PLINK_HOLDING: + return NL80211_PLINK_HOLDING; + case PLINK_BLOCKED: + return NL80211_PLINK_BLOCKED; + default: + wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d", + state); } + return -1; } +#endif /* CONFIG_MESH */ -static int wpa_driver_nl80211_authenticate( - void *priv, struct wpa_driver_auth_params *params) +static int wpa_driver_nl80211_sta_add(void *priv, + struct hostapd_sta_add_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1, i; struct nl_msg *msg; - enum nl80211_auth_type type; - enum nl80211_iftype nlmode; - int count = 0; - int is_retry; - - is_retry = drv->retry_auth; - drv->retry_auth = 0; + struct nl80211_sta_flag_update upd; + int ret = -ENOBUFS; - drv->associated = 0; - os_memset(drv->auth_bssid, 0, ETH_ALEN); - /* FIX: IBSS mode */ - nlmode = params->p2p ? - NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; - if (drv->nlmode != nlmode && - wpa_driver_nl80211_set_mode(priv, nlmode) < 0) - return -1; + if ((params->flags & WPA_STA_TDLS_PEER) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; -retry: - msg = nlmsg_alloc(); - if (!msg) - return -1; + wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, + params->set ? "Set" : "Add", MAC2STR(params->addr)); + msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION); + if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr)) + goto fail; - wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", - drv->ifindex); + if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) { + wpa_hexdump(MSG_DEBUG, " * supported rates", + params->supp_rates, params->supp_rates_len); + wpa_printf(MSG_DEBUG, " * capability=0x%x", + params->capability); + if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES, + params->supp_rates_len, params->supp_rates) || + nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY, + params->capability)) + goto fail; - nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE); + if (params->ht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * ht_capabilities", + (u8 *) params->ht_capabilities, + sizeof(*params->ht_capabilities)); + if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, + sizeof(*params->ht_capabilities), + params->ht_capabilities)) + goto fail; + } - for (i = 0; i < 4; i++) { - if (!params->wep_key[i]) - continue; - wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP, - NULL, i, - i == params->wep_tx_keyidx, NULL, 0, - params->wep_key[i], - params->wep_key_len[i]); - if (params->wep_tx_keyidx != i) - continue; - if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0, - params->wep_key[i], params->wep_key_len[i])) { - nlmsg_free(msg); - return -1; + if (params->vht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * vht_capabilities", + (u8 *) params->vht_capabilities, + sizeof(*params->vht_capabilities)); + if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, + sizeof(*params->vht_capabilities), + params->vht_capabilities)) + goto fail; } - } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (params->bssid) { - wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, - MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); - } - if (params->freq) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + if (params->ext_capab) { + wpa_hexdump(MSG_DEBUG, " * ext_capab", + params->ext_capab, params->ext_capab_len); + if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY, + params->ext_capab_len, params->ext_capab)) + goto fail; + } } - if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); + if (!params->set) { + if (params->aid) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid)) + goto fail; + } else { + /* + * cfg80211 validates that AID is non-zero, so we have + * to make this a non-zero value for the TDLS case where + * a dummy STA entry is used for now. + */ + wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1)) + goto fail; + } + wpa_printf(MSG_DEBUG, " * listen_interval=%u", + params->listen_interval); + if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval)) + goto fail; + } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) { + wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); + if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid)) + goto fail; } - wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); - if (params->ie) - NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); - if (params->sae_data) { - wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, - params->sae_data_len); - NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, - params->sae_data); + + if (params->vht_opmode_enabled) { + wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode); + if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF, + params->vht_opmode)) + goto fail; } - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else if (params->auth_alg & WPA_AUTH_ALG_SAE) - type = NL80211_AUTHTYPE_SAE; - else - goto nla_put_failure; - wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); - if (params->local_state_change) { - wpa_printf(MSG_DEBUG, " * Local state change only"); - NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); + + if (params->supp_channels) { + wpa_hexdump(MSG_DEBUG, " * supported channels", + params->supp_channels, params->supp_channels_len); + if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS, + params->supp_channels_len, params->supp_channels)) + goto fail; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: MLME command failed (auth): ret=%d (%s)", - ret, strerror(-ret)); - count++; - if (ret == -EALREADY && count == 1 && params->bssid && - !params->local_state_change) { - /* - * mac80211 does not currently accept new - * authentication if we are already authenticated. As a - * workaround, force deauthentication and try again. - */ - wpa_printf(MSG_DEBUG, "nl80211: Retry authentication " - "after forced deauthentication"); - wpa_driver_nl80211_deauthenticate( - bss, params->bssid, - WLAN_REASON_PREV_AUTH_NOT_VALID); - nlmsg_free(msg); - goto retry; - } + if (params->supp_oper_classes) { + wpa_hexdump(MSG_DEBUG, " * supported operating classes", + params->supp_oper_classes, + params->supp_oper_classes_len); + if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + params->supp_oper_classes_len, + params->supp_oper_classes)) + goto fail; + } - if (ret == -ENOENT && params->freq && !is_retry) { - /* - * cfg80211 has likely expired the BSS entry even - * though it was previously available in our internal - * BSS table. To recover quickly, start a single - * channel scan on the specified channel. - */ - struct wpa_driver_scan_params scan; - int freqs[2]; + os_memset(&upd, 0, sizeof(upd)); + upd.set = sta_flags_nl80211(params->flags); + upd.mask = upd.set | sta_flags_nl80211(params->flags_mask); + wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", + upd.set, upd.mask); + if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) + goto fail; - os_memset(&scan, 0, sizeof(scan)); - scan.num_ssids = 1; - if (params->ssid) { - scan.ssids[0].ssid = params->ssid; - scan.ssids[0].ssid_len = params->ssid_len; - } - freqs[0] = params->freq; - freqs[1] = 0; - scan.freqs = freqs; - wpa_printf(MSG_DEBUG, "nl80211: Trigger single " - "channel scan to refresh cfg80211 BSS " - "entry"); - ret = wpa_driver_nl80211_scan(bss, &scan); - if (ret == 0) { - nl80211_copy_auth_params(drv, params); - drv->scan_for_auth = 1; - } - } else if (is_retry) { - /* - * Need to indicate this with an event since the return - * value from the retry is not delivered to core code. - */ - union wpa_event_data event; - wpa_printf(MSG_DEBUG, "nl80211: Authentication retry " - "failed"); - os_memset(&event, 0, sizeof(event)); - os_memcpy(event.timeout_event.addr, drv->auth_bssid_, - ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, - &event); - } +#ifdef CONFIG_MESH + if (params->plink_state && + nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE, + sta_plink_state_nl80211(params->plink_state))) + goto fail; +#endif /* CONFIG_MESH */ - goto nla_put_failure; + if (params->flags & WPA_STA_WMM) { + struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); + + wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); + if (!wme || + nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES, + params->qosinfo & WMM_QOSINFO_STA_AC_MASK) || + nla_put_u8(msg, NL80211_STA_WME_MAX_SP, + (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & + WMM_QOSINFO_STA_SP_MASK)) + goto fail; + nla_nest_end(msg, wme); } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Authentication request send " - "successfully"); -nla_put_failure: + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " + "result: %d (%s)", params->set ? "SET" : "NEW", ret, + strerror(-ret)); + if (ret == -EEXIST) + ret = 0; +fail: nlmsg_free(msg); return ret; } -static int wpa_driver_nl80211_authenticate_retry( - struct wpa_driver_nl80211_data *drv) +static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) { - struct wpa_driver_auth_params params; - struct i802_bss *bss = &drv->first_bss; - int i; - - wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again"); - - os_memset(¶ms, 0, sizeof(params)); - params.freq = drv->auth_freq; - params.auth_alg = drv->auth_alg; - params.wep_tx_keyidx = drv->auth_wep_tx_keyidx; - params.local_state_change = drv->auth_local_state_change; - params.p2p = drv->auth_p2p; +#ifdef CONFIG_LIBNL3_ROUTE + struct wpa_driver_nl80211_data *drv = bss->drv; + struct rtnl_neigh *rn; + struct nl_addr *nl_addr; + int err; - if (!is_zero_ether_addr(drv->auth_bssid_)) - params.bssid = drv->auth_bssid_; + rn = rtnl_neigh_alloc(); + if (!rn) + return; - if (drv->auth_ssid_len) { - params.ssid = drv->auth_ssid; - params.ssid_len = drv->auth_ssid_len; + rtnl_neigh_set_family(rn, AF_BRIDGE); + rtnl_neigh_set_ifindex(rn, bss->ifindex); + nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN); + if (!nl_addr) { + rtnl_neigh_put(rn); + return; } + rtnl_neigh_set_lladdr(rn, nl_addr); - params.ie = drv->auth_ie; - params.ie_len = drv->auth_ie_len; - - for (i = 0; i < 4; i++) { - if (drv->auth_wep_key_len[i]) { - params.wep_key[i] = drv->auth_wep_key[i]; - params.wep_key_len[i] = drv->auth_wep_key_len[i]; - } + err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0); + if (err < 0) { + wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for " + MACSTR " ifindex=%d failed: %s", MAC2STR(addr), + bss->ifindex, nl_geterror(err)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for " + MACSTR, MAC2STR(addr)); } - drv->retry_auth = 1; - return wpa_driver_nl80211_authenticate(bss, ¶ms); + nl_addr_put(nl_addr); + rtnl_neigh_put(rn); +#endif /* CONFIG_LIBNL3_ROUTE */ } -struct phy_info_arg { - u16 *num_modes; - struct hostapd_hw_modes *modes; -}; - -static int phy_info_handler(struct nl_msg *msg, void *arg) +static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr, + int deauth, u16 reason_code) { - struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct phy_info_arg *phy_info = arg; - - struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; - - struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { - [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, - [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, - }; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; - struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; - static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { - [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, - [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, - }; + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + (deauth == 0 && + nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE, + WLAN_FC_STYPE_DISASSOC)) || + (deauth == 1 && + nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE, + WLAN_FC_STYPE_DEAUTH)) || + (reason_code && + nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) { + nlmsg_free(msg); + return -ENOBUFS; + } - struct nlattr *nl_band; - struct nlattr *nl_freq; - struct nlattr *nl_rate; - int rem_band, rem_freq, rem_rate; - struct hostapd_hw_modes *mode; - int idx, mode_is_set; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR + " --> %d (%s)", + bss->ifname, MAC2STR(addr), ret, strerror(-ret)); - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + if (drv->rtnl_sk) + rtnl_neigh_delete_fdb_entry(bss, addr); - if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) - return NL_SKIP; + if (ret == -ENOENT) + return 0; + return ret; +} - nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { - mode = os_realloc_array(phy_info->modes, - *phy_info->num_modes + 1, - sizeof(*mode)); - if (!mode) - return NL_SKIP; - phy_info->modes = mode; - mode_is_set = 0; +void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv2; - mode = &phy_info->modes[*(phy_info->num_modes)]; - memset(mode, 0, sizeof(*mode)); - mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN; - *(phy_info->num_modes) += 1; + wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); - nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), - nla_len(nl_band), NULL); + /* stop listening for EAPOL on this interface */ + dl_list_for_each(drv2, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) + del_ifidx(drv2, ifidx); - if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { - mode->ht_capab = nla_get_u16( - tb_band[NL80211_BAND_ATTR_HT_CAPA]); - } + msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE); + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; + wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); +} - if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) { - mode->a_mpdu_params |= nla_get_u8( - tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) & - 0x03; - } - if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) { - mode->a_mpdu_params |= nla_get_u8( - tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) << - 2; - } +static const char * nl80211_iftype_str(enum nl80211_iftype mode) +{ + switch (mode) { + case NL80211_IFTYPE_ADHOC: + return "ADHOC"; + case NL80211_IFTYPE_STATION: + return "STATION"; + case NL80211_IFTYPE_AP: + return "AP"; + case NL80211_IFTYPE_AP_VLAN: + return "AP_VLAN"; + case NL80211_IFTYPE_WDS: + return "WDS"; + case NL80211_IFTYPE_MONITOR: + return "MONITOR"; + case NL80211_IFTYPE_MESH_POINT: + return "MESH_POINT"; + case NL80211_IFTYPE_P2P_CLIENT: + return "P2P_CLIENT"; + case NL80211_IFTYPE_P2P_GO: + return "P2P_GO"; + case NL80211_IFTYPE_P2P_DEVICE: + return "P2P_DEVICE"; + default: + return "unknown"; + } +} - if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] && - nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) { - u8 *mcs; - mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); - os_memcpy(mode->mcs_set, mcs, 16); - } - if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) { - mode->vht_capab = nla_get_u32( - tb_band[NL80211_BAND_ATTR_VHT_CAPA]); - } +static int nl80211_create_iface_once(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) +{ + struct nl_msg *msg; + int ifidx; + int ret = -ENOBUFS; - if (tb_band[NL80211_BAND_ATTR_VHT_MCS_SET] && - nla_len(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])) { - u8 *mcs; - mcs = nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); - os_memcpy(mode->vht_mcs_set, mcs, 8); - } + wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", + iftype, nl80211_iftype_str(iftype)); - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), - nla_len(nl_freq), freq_policy); - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - mode->num_channels++; - } + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE); + if (!msg || + nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) || + nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype)) + goto fail; - mode->channels = os_calloc(mode->num_channels, - sizeof(struct hostapd_channel_data)); - if (!mode->channels) - return NL_SKIP; - - idx = 0; - - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), - nla_len(nl_freq), freq_policy); - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - - mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - mode->channels[idx].flag = 0; - - if (!mode_is_set) { - /* crude heuristic */ - if (mode->channels[idx].freq < 4000) - mode->mode = HOSTAPD_MODE_IEEE80211B; - else if (mode->channels[idx].freq > 50000) - mode->mode = HOSTAPD_MODE_IEEE80211AD; - else - mode->mode = HOSTAPD_MODE_IEEE80211A; - mode_is_set = 1; - } + if (iftype == NL80211_IFTYPE_MONITOR) { + struct nlattr *flags; - switch (mode->mode) { - case HOSTAPD_MODE_IEEE80211AD: - mode->channels[idx].chan = - (mode->channels[idx].freq - 56160) / - 2160; - break; - case HOSTAPD_MODE_IEEE80211A: - mode->channels[idx].chan = - mode->channels[idx].freq / 5 - 1000; - break; - case HOSTAPD_MODE_IEEE80211B: - case HOSTAPD_MODE_IEEE80211G: - if (mode->channels[idx].freq == 2484) - mode->channels[idx].chan = 14; - else - mode->channels[idx].chan = - (mode->channels[idx].freq - - 2407) / 5; - break; - default: - break; - } + flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS); + if (!flags || + nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES)) + goto fail; - if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_DISABLED; - if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_PASSIVE_SCAN; - if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_NO_IBSS; - if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) - mode->channels[idx].flag |= - HOSTAPD_CHAN_RADAR; - - if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && - !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - mode->channels[idx].max_tx_power = - nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100; - - idx++; - } + nla_nest_end(msg, flags); + } else if (wds) { + if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds)) + goto fail; + } - nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), - nla_len(nl_rate), rate_policy); - if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) - continue; - mode->num_rates++; - } + /* + * Tell cfg80211 that the interface belongs to the socket that created + * it, and the interface should be deleted when the socket is closed. + */ + if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) + goto fail; - mode->rates = os_calloc(mode->num_rates, sizeof(int)); - if (!mode->rates) - return NL_SKIP; + ret = send_and_recv_msgs(drv, msg, handler, arg); + msg = NULL; + if (ret) { + fail: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", + ifname, ret, strerror(-ret)); + return ret; + } - idx = 0; + if (iftype == NL80211_IFTYPE_P2P_DEVICE) + return 0; - nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), - nla_len(nl_rate), rate_policy); - if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) - continue; - mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); + ifidx = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", + ifname, ifidx); - /* crude heuristic */ - if (mode->mode == HOSTAPD_MODE_IEEE80211B && - mode->rates[idx] > 200) - mode->mode = HOSTAPD_MODE_IEEE80211G; + if (ifidx <= 0) + return -1; - idx++; - } + /* + * Some virtual interfaces need to process EAPOL packets and events on + * the parent interface. This is used mainly with hostapd. + */ + if (drv->hostapd || + iftype == NL80211_IFTYPE_AP_VLAN || + iftype == NL80211_IFTYPE_WDS || + iftype == NL80211_IFTYPE_MONITOR) { + /* start listening for EAPOL on this interface */ + add_ifidx(drv, ifidx); } - return NL_SKIP; -} - -static struct hostapd_hw_modes * -wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes) -{ - u16 m; - struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; - int i, mode11g_idx = -1; - - /* If only 802.11g mode is included, use it to construct matching - * 802.11b mode data. */ - - for (m = 0; m < *num_modes; m++) { - if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) - return modes; /* 802.11b already included */ - if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) - mode11g_idx = m; + if (addr && iftype != NL80211_IFTYPE_MONITOR && + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) { + nl80211_remove_iface(drv, ifidx); + return -1; } - if (mode11g_idx < 0) - return modes; /* 2.4 GHz band not supported at all */ + return ifidx; +} - nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); - if (nmodes == NULL) - return modes; /* Could not add 802.11b mode */ - mode = &nmodes[*num_modes]; - os_memset(mode, 0, sizeof(*mode)); - (*num_modes)++; - modes = nmodes; +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) +{ + int ret; - mode->mode = HOSTAPD_MODE_IEEE80211B; + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, + arg); - mode11g = &modes[mode11g_idx]; - mode->num_channels = mode11g->num_channels; - mode->channels = os_malloc(mode11g->num_channels * - sizeof(struct hostapd_channel_data)); - if (mode->channels == NULL) { - (*num_modes)--; - return modes; /* Could not add 802.11b mode */ - } - os_memcpy(mode->channels, mode11g->channels, - mode11g->num_channels * sizeof(struct hostapd_channel_data)); + /* if error occurred and interface exists already */ + if (ret == -ENFILE && if_nametoindex(ifname)) { + if (use_existing) { + wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s", + ifname); + if (addr && iftype != NL80211_IFTYPE_MONITOR && + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, + addr) < 0 && + (linux_set_iface_flags(drv->global->ioctl_sock, + ifname, 0) < 0 || + linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, + addr) < 0 || + linux_set_iface_flags(drv->global->ioctl_sock, + ifname, 1) < 0)) + return -1; + return -ENFILE; + } + wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); - mode->num_rates = 0; - mode->rates = os_malloc(4 * sizeof(int)); - if (mode->rates == NULL) { - os_free(mode->channels); - (*num_modes)--; - return modes; /* Could not add 802.11b mode */ - } + /* Try to remove the interface that was already there. */ + nl80211_remove_iface(drv, if_nametoindex(ifname)); - for (i = 0; i < mode11g->num_rates; i++) { - if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && - mode11g->rates[i] != 55 && mode11g->rates[i] != 110) - continue; - mode->rates[mode->num_rates] = mode11g->rates[i]; - mode->num_rates++; - if (mode->num_rates == 4) - break; + /* Try to create the interface again */ + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, + wds, handler, arg); } - if (mode->num_rates == 0) { - os_free(mode->channels); - os_free(mode->rates); - (*num_modes)--; - return modes; /* No 802.11b rates */ + if (ret >= 0 && is_p2p_net_interface(iftype)) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s created for P2P - disable 11b rates", + ifname); + nl80211_disable_11b_rates(drv, ret, 1); } - wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " - "information"); - - return modes; + return ret; } -static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, - int end) +static int nl80211_setup_ap(struct i802_bss *bss) { - int c; + struct wpa_driver_nl80211_data *drv = bss->drv; - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if (chan->freq - 10 >= start && chan->freq + 10 <= end) - chan->flag |= HOSTAPD_CHAN_HT40; - } -} + wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d", + bss->ifname, drv->device_ap_sme, drv->use_monitor); + /* + * Disable Probe Request reporting unless we need it in this way for + * devices that include the AP SME, in the other case (unless using + * monitor iface) we'll get it through the nl_mgmt socket instead. + */ + if (!drv->device_ap_sme) + wpa_driver_nl80211_probe_req_report(bss, 0); -static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, - int end) -{ - int c; + if (!drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap(bss)) + return -1; - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if (!(chan->flag & HOSTAPD_CHAN_HT40)) - continue; - if (chan->freq - 30 >= start && chan->freq - 10 <= end) - chan->flag |= HOSTAPD_CHAN_HT40MINUS; - if (chan->freq + 10 >= start && chan->freq + 30 <= end) - chan->flag |= HOSTAPD_CHAN_HT40PLUS; + if (drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) + return -1; + + if (!drv->device_ap_sme && drv->use_monitor && + nl80211_create_monitor_interface(drv) && + !drv->device_ap_sme) + return -1; + + if (drv->device_ap_sme && + wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " + "Probe Request frame reporting in AP mode"); + /* Try to survive without this */ } + + return 0; } -static void nl80211_reg_rule_ht40(struct nlattr *tb[], - struct phy_info_arg *results) +static void nl80211_teardown_ap(struct i802_bss *bss) { - u32 start, end, max_bw; - u16 m; - - if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) - return; - - start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; - end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; - max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + struct wpa_driver_nl80211_data *drv = bss->drv; - wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz", - start, end, max_bw); - if (max_bw < 40) - return; + wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d", + bss->ifname, drv->device_ap_sme, drv->use_monitor); + if (drv->device_ap_sme) { + wpa_driver_nl80211_probe_req_report(bss, 0); + if (!drv->use_monitor) + nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); + } else if (drv->use_monitor) + nl80211_remove_monitor_interface(drv); + else + nl80211_mgmt_unsubscribe(bss, "AP teardown"); - for (m = 0; m < *results->num_modes; m++) { - if (!(results->modes[m].ht_capab & - HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) - continue; - nl80211_set_ht40_mode(&results->modes[m], start, end); - } + bss->beacon_set = 0; } -static void nl80211_reg_rule_sec(struct nlattr *tb[], - struct phy_info_arg *results) +static int nl80211_send_eapol_data(struct i802_bss *bss, + const u8 *addr, const u8 *data, + size_t data_len) { - u32 start, end, max_bw; - u16 m; - - if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) - return; + struct sockaddr_ll ll; + int ret; - start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; - end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; - max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + if (bss->drv->eapol_tx_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL"); + return -1; + } - if (max_bw < 20) - return; + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = bss->ifindex; + ll.sll_protocol = htons(ETH_P_PAE); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, addr, ETH_ALEN); + ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0, + (struct sockaddr *) &ll, sizeof(ll)); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s", + strerror(errno)); - for (m = 0; m < *results->num_modes; m++) { - if (!(results->modes[m].ht_capab & - HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) - continue; - nl80211_set_ht40_mode_sec(&results->modes[m], start, end); - } + return ret; } -static int nl80211_get_reg(struct nl_msg *msg, void *arg) +static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +static int wpa_driver_nl80211_hapd_send_eapol( + void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr, u32 flags) { - struct phy_info_arg *results = arg; - struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *nl_rule; - struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; - int rem_rule; - static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { - [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, - }; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + int qos = flags & WPA_STA_WMM; - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || - !tb_msg[NL80211_ATTR_REG_RULES]) { - wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " - "available"); - return NL_SKIP; + if (drv->device_ap_sme || !drv->use_monitor) + return nl80211_send_eapol_data(bss, addr, data, data_len); + + len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + if (qos) { + hdr->frame_control |= + host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); } - wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", - (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); - - nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) - { - nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_rule), nla_len(nl_rule), reg_policy); - nl80211_reg_rule_ht40(tb_rule, results); - } + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + pos = (u8 *) (hdr + 1); - nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) - { - nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_rule), nla_len(nl_rule), reg_policy); - nl80211_reg_rule_sec(tb_rule, results); + if (qos) { + /* Set highest priority in QoS header */ + pos[0] = 7; + pos[1] = 0; + pos += 2; } - return NL_SKIP; -} - - -static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv, - struct phy_info_arg *results) -{ - struct nl_msg *msg; + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + WPA_PUT_BE16(pos, ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, + 0, 0, 0, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " + "failed: %d (%s)", + (unsigned long) len, errno, strerror(errno)); + } + os_free(hdr); - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); - return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); + return res; } -static struct hostapd_hw_modes * -wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, + int total_flags, + int flags_or, int flags_and) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - struct phy_info_arg result = { - .num_modes = num_modes, - .modes = NULL, - }; + struct nlattr *flags; + struct nl80211_sta_flag_update upd; - *num_modes = 0; - *flags = 0; + wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR + " total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d", + bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and, + !!(total_flags & WPA_STA_AUTHORIZED)); - msg = nlmsg_alloc(); - if (!msg) - return NULL; + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + goto fail; + + /* + * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This + * can be removed eventually. + */ + flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS); + if (!flags || + ((total_flags & WPA_STA_AUTHORIZED) && + nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) || + ((total_flags & WPA_STA_WMM) && + nla_put_flag(msg, NL80211_STA_FLAG_WME)) || + ((total_flags & WPA_STA_SHORT_PREAMBLE) && + nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) || + ((total_flags & WPA_STA_MFP) && + nla_put_flag(msg, NL80211_STA_FLAG_MFP)) || + ((total_flags & WPA_STA_TDLS_PEER) && + nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER))) + goto fail; - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + nla_nest_end(msg, flags); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + os_memset(&upd, 0, sizeof(upd)); + upd.mask = sta_flags_nl80211(flags_or | ~flags_and); + upd.set = sta_flags_nl80211(flags_or); + if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) + goto fail; - if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { - nl80211_set_ht40_flags(drv, &result); - return wpa_driver_nl80211_add_11b(result.modes, num_modes); - } - msg = NULL; - nla_put_failure: + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); +fail: nlmsg_free(msg); - return NULL; + return -ENOBUFS; } -static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, - const void *data, size_t len, - int encrypt, int noack) +static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) { - __u8 rtap_hdr[] = { - 0x00, 0x00, /* radiotap version */ - 0x0e, 0x00, /* radiotap length */ - 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ - IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ - 0x00, /* padding */ - 0x00, 0x00, /* RX and TX flags to indicate that */ - 0x00, 0x00, /* this is the injected frame directly */ - }; - struct iovec iov[2] = { - { - .iov_base = &rtap_hdr, - .iov_len = sizeof(rtap_hdr), - }, - { - .iov_base = (void *) data, - .iov_len = len, - } - }; - struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = iov, - .msg_iovlen = 2, - .msg_control = NULL, - .msg_controllen = 0, - .msg_flags = 0, - }; - int res; - u16 txflags = 0; + enum nl80211_iftype nlmode, old_mode; - if (encrypt) - rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + if (params->p2p) { + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); + nlmode = NL80211_IFTYPE_P2P_GO; + } else + nlmode = NL80211_IFTYPE_AP; - if (drv->monitor_sock < 0) { - wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " - "for %s", __func__); + old_mode = drv->nlmode; + if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) { + nl80211_remove_monitor_interface(drv); return -1; } - if (noack) - txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; - WPA_PUT_LE16(&rtap_hdr[12], txflags); - - res = sendmsg(drv->monitor_sock, &msg, 0); - if (res < 0) { - wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); + if (params->freq.freq && + nl80211_set_channel(drv->first_bss, ¶ms->freq, 0)) { + if (old_mode != nlmode) + wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); + nl80211_remove_monitor_interface(drv); return -1; } + return 0; } -static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, - const void *data, size_t len, - int encrypt, int noack, - unsigned int freq, int no_cck, - int offchanok, unsigned int wait_time) +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, + int reset_mode) { - struct wpa_driver_nl80211_data *drv = bss->drv; - u64 cookie; + struct nl_msg *msg; + int ret; - if (freq == 0) - freq = bss->freq; + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " + "(%s)", ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Leave IBSS request sent successfully"); + } - if (drv->use_monitor) - return wpa_driver_nl80211_send_mntr(drv, data, len, - encrypt, noack); + if (reset_mode && + wpa_driver_nl80211_set_mode(drv->first_bss, + NL80211_IFTYPE_STATION)) { + wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " + "station mode"); + } - return nl80211_send_frame_cmd(bss, freq, wait_time, data, len, - &cookie, no_cck, noack, offchanok); + return ret; } -static int wpa_driver_nl80211_send_mlme_freq(struct i802_bss *bss, - const u8 *data, - size_t data_len, int noack, - unsigned int freq, int no_cck, - int offchanok, - unsigned int wait_time) +static int nl80211_ht_vht_overrides(struct nl_msg *msg, + struct wpa_driver_associate_params *params) { - struct wpa_driver_nl80211_data *drv = bss->drv; - struct ieee80211_mgmt *mgmt; - int encrypt = 1; - u16 fc; - - mgmt = (struct ieee80211_mgmt *) data; - fc = le_to_host16(mgmt->frame_control); + if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT)) + return -1; - if (is_sta_interface(drv->nlmode) && - WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { - /* - * The use of last_mgmt_freq is a bit of a hack, - * but it works due to the single-threaded nature - * of wpa_supplicant. - */ - if (freq == 0) - freq = drv->last_mgmt_freq; - return nl80211_send_frame_cmd(bss, freq, 0, - data, data_len, NULL, 1, noack, - 1); + if (params->htcaps && params->htcaps_mask) { + int sz = sizeof(struct ieee80211_ht_capabilities); + wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz); + wpa_hexdump(MSG_DEBUG, " * htcaps_mask", + params->htcaps_mask, sz); + if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz, + params->htcaps) || + nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask)) + return -1; } - if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { - if (freq == 0) - freq = bss->freq; - return nl80211_send_frame_cmd(bss, freq, - (int) freq == bss->freq ? 0 : - wait_time, - data, data_len, - &drv->send_action_cookie, - no_cck, noack, offchanok); +#ifdef CONFIG_VHT_OVERRIDES + if (params->disable_vht) { + wpa_printf(MSG_DEBUG, " * VHT disabled"); + if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT)) + return -1; } - if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { - /* - * Only one of the authentication frame types is encrypted. - * In order for static WEP encryption to work properly (i.e., - * to not encrypt the frame), we need to tell mac80211 about - * the frames that must not be encrypted. - */ - u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); - if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3) - encrypt = 0; + if (params->vhtcaps && params->vhtcaps_mask) { + int sz = sizeof(struct ieee80211_vht_capabilities); + wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz); + wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask", + params->vhtcaps_mask, sz); + if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz, + params->vhtcaps) || + nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, + params->vhtcaps_mask)) + return -1; } +#endif /* CONFIG_VHT_OVERRIDES */ - return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, - noack, freq, no_cck, offchanok, - wait_time); + return 0; } -static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len, int noack) +static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) { - struct i802_bss *bss = priv; - return wpa_driver_nl80211_send_mlme_freq(bss, data, data_len, noack, - 0, 0, 0, 0); -} + struct nl_msg *msg; + int ret = -1; + int count = 0; + wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); -static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, - int slot, int ht_opmode, int ap_isolate, - int *basic_rates) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; + if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, ¶ms->freq)) { + wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " + "IBSS mode"); + return -1; + } - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; +retry: + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) || + params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) + goto fail; + + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) + goto fail; + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + + if (nl80211_put_freq_params(msg, ¶ms->freq) < 0 || + nl80211_put_beacon_int(msg, params->beacon_int)) + goto fail; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + ret = nl80211_set_conn_keys(params, msg); + if (ret) + goto fail; - if (cts >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); - if (preamble >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); - if (slot >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - if (ht_opmode >= 0) - NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); - if (ap_isolate >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate); + if (params->bssid && params->fixed_bssid) { + wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, + MAC2STR(params->bssid)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + goto fail; + } - if (basic_rates) { - u8 rates[NL80211_MAX_SUPP_RATES]; - u8 rates_len = 0; - int i; + if (params->fixed_freq) { + wpa_printf(MSG_DEBUG, " * fixed_freq"); + if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED)) + goto fail; + } - for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; - i++) - rates[rates_len++] = basic_rates[i] / 5; + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) { + wpa_printf(MSG_DEBUG, " * control port"); + if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) + goto fail; + } - NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + if (params->wpa_ie) { + wpa_hexdump(MSG_DEBUG, + " * Extra IEs for Beacon/Probe Response frames", + params->wpa_ie, params->wpa_ie_len); + if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie)) + goto fail; } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (nl80211_ht_vht_overrides(msg, params) < 0) + return -1; - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)", + ret, strerror(-ret)); + count++; + if (ret == -EALREADY && count == 1) { + wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after " + "forced leave"); + nl80211_leave_ibss(drv, 0); + nlmsg_free(msg); + goto retry; + } + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Join IBSS request sent successfully"); + } + +fail: nlmsg_free(msg); - return -ENOBUFS; + return ret; } -static int wpa_driver_nl80211_set_ap(void *priv, - struct wpa_driver_ap_params *params) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - u8 cmd = NL80211_CMD_NEW_BEACON; - int ret; - int beacon_set; - int ifindex = if_nametoindex(bss->ifname); - int num_suites; - u32 suites[10]; - u32 ver; +static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params, + struct nl_msg *msg) +{ + if (params->bssid) { + wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, + MAC2STR(params->bssid)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + return -1; + } + + if (params->bssid_hint) { + wpa_printf(MSG_DEBUG, " * bssid_hint=" MACSTR, + MAC2STR(params->bssid_hint)); + if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN, + params->bssid_hint)) + return -1; + } + + if (params->freq.freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + params->freq.freq)) + return -1; + drv->assoc_freq = params->freq.freq; + } else + drv->assoc_freq = 0; + + if (params->freq_hint) { + wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT, + params->freq_hint)) + return -1; + } + + if (params->bg_scan_period >= 0) { + wpa_printf(MSG_DEBUG, " * bg scan period=%d", + params->bg_scan_period); + if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD, + params->bg_scan_period)) + return -1; + } + + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->ssid, params->ssid_len); + if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid)) + return -1; + if (params->ssid_len > sizeof(drv->ssid)) + return -1; + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + } + + wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); + if (params->wpa_ie && + nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie)) + return -1; + + if (params->wpa_proto) { + enum nl80211_wpa_versions ver = 0; - beacon_set = bss->beacon_set; + if (params->wpa_proto & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_proto & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); + if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver)) + return -1; + } - wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", - beacon_set); - if (beacon_set) - cmd = NL80211_CMD_SET_BEACON; + if (params->pairwise_suite != WPA_CIPHER_NONE) { + u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite); + wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); + if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + cipher)) + return -1; + } - nl80211_cmd(drv, msg, 0, cmd); - NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); - NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); - if (params->proberesp && params->proberesp_len) - NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, - params->proberesp); - switch (params->hide_ssid) { - case NO_SSID_HIDING: - NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, - NL80211_HIDDEN_SSID_NOT_IN_USE); - break; - case HIDDEN_SSID_ZERO_LEN: - NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, - NL80211_HIDDEN_SSID_ZERO_LEN); - break; - case HIDDEN_SSID_ZERO_CONTENTS: - NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, - NL80211_HIDDEN_SSID_ZERO_CONTENTS); - break; + if (params->group_suite == WPA_CIPHER_GTK_NOT_USED && + !(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) { + /* + * This is likely to work even though many drivers do not + * advertise support for operations without GTK. + */ + wpa_printf(MSG_DEBUG, " * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement"); + } else if (params->group_suite != WPA_CIPHER_NONE) { + u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite); + wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); + if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher)) + return -1; } - if (params->privacy) - NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); - if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == - (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { - /* Leave out the attribute */ - } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, - NL80211_AUTHTYPE_SHARED_KEY); - else - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, - NL80211_AUTHTYPE_OPEN_SYSTEM); - ver = 0; - if (params->wpa_version & WPA_PROTO_WPA) - ver |= NL80211_WPA_VERSION_1; - if (params->wpa_version & WPA_PROTO_RSN) - ver |= NL80211_WPA_VERSION_2; - if (ver) - NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_CCKM || + params->key_mgmt_suite == WPA_KEY_MGMT_OSEN || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + int mgmt = WLAN_AKM_SUITE_PSK; - num_suites = 0; - if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) - suites[num_suites++] = WLAN_AKM_SUITE_8021X; - if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) - suites[num_suites++] = WLAN_AKM_SUITE_PSK; - if (num_suites) { - NLA_PUT(msg, NL80211_ATTR_AKM_SUITES, - num_suites * sizeof(u32), suites); + switch (params->key_mgmt_suite) { + case WPA_KEY_MGMT_CCKM: + mgmt = WLAN_AKM_SUITE_CCKM; + break; + case WPA_KEY_MGMT_IEEE8021X: + mgmt = WLAN_AKM_SUITE_8021X; + break; + case WPA_KEY_MGMT_FT_IEEE8021X: + mgmt = WLAN_AKM_SUITE_FT_8021X; + break; + case WPA_KEY_MGMT_FT_PSK: + mgmt = WLAN_AKM_SUITE_FT_PSK; + break; + case WPA_KEY_MGMT_IEEE8021X_SHA256: + mgmt = WLAN_AKM_SUITE_8021X_SHA256; + break; + case WPA_KEY_MGMT_PSK_SHA256: + mgmt = WLAN_AKM_SUITE_PSK_SHA256; + break; + case WPA_KEY_MGMT_OSEN: + mgmt = WLAN_AKM_SUITE_OSEN; + break; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + mgmt = WLAN_AKM_SUITE_8021X_SUITE_B; + break; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192; + break; + case WPA_KEY_MGMT_PSK: + default: + mgmt = WLAN_AKM_SUITE_PSK; + break; + } + wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt); + if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt)) + return -1; } - if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X && - params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT); + if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) + return -1; - num_suites = 0; - if (params->pairwise_ciphers & WPA_CIPHER_CCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; - if (params->pairwise_ciphers & WPA_CIPHER_GCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; - if (params->pairwise_ciphers & WPA_CIPHER_TKIP) - suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; - if (params->pairwise_ciphers & WPA_CIPHER_WEP104) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; - if (params->pairwise_ciphers & WPA_CIPHER_WEP40) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; - if (num_suites) { - NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, - num_suites * sizeof(u32), suites); - } + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + return -1; - switch (params->group_cipher) { - case WPA_CIPHER_CCMP: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_CCMP); - break; - case WPA_CIPHER_GCMP: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_GCMP); - break; - case WPA_CIPHER_TKIP: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_TKIP); - break; - case WPA_CIPHER_WEP104: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_WEP104); - break; - case WPA_CIPHER_WEP40: - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - WLAN_CIPHER_SUITE_WEP40); - break; + if (params->rrm_used) { + u32 drv_rrm_flags = drv->capa.rrm_flags; + if (!(drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || + !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) || + nla_put_flag(msg, NL80211_ATTR_USE_RRM)) + return -1; } - if (params->beacon_ies) { - NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), - wpabuf_head(params->beacon_ies)); - } - if (params->proberesp_ies) { - NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, - wpabuf_len(params->proberesp_ies), - wpabuf_head(params->proberesp_ies)); - } - if (params->assocresp_ies) { - NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, - wpabuf_len(params->assocresp_ies), - wpabuf_head(params->assocresp_ies)); - } + if (nl80211_ht_vht_overrides(msg, params) < 0) + return -1; - if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { - NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, - params->ap_max_inactivity); - } + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", - ret, strerror(-ret)); - } else { - bss->beacon_set = 1; - nl80211_set_bss(bss, params->cts_protect, params->preamble, - params->short_slot_time, params->ht_opmode, - params->isolate, params->basic_rates); - } - return ret; - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + return 0; } -static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, - int freq, int ht_enabled, - int sec_channel_offset) +static int wpa_driver_nl80211_try_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) { - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + enum nl80211_auth_type type; int ret; + int algs; - wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d " - "sec_channel_offset=%d)", - freq, ht_enabled, sec_channel_offset); - msg = nlmsg_alloc(); + if (params->req_key_mgmt_offload && params->psk && + (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)) { + wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); + ret = issue_key_mgmt_set_key(drv, params->psk, 32); + if (ret) + return ret; + } + + wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); if (!msg) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + ret = nl80211_connect_common(drv, params, msg); + if (ret) + goto fail; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - if (ht_enabled) { - switch (sec_channel_offset) { - case -1: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT40MINUS); - break; - case 1: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT40PLUS); - break; - default: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT20); - break; - } + algs = 0; + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_SHARED) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_LEAP) + algs++; + if (algs > 1) { + wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " + "selection"); + goto skip_auth_type; } + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + else if (params->auth_alg & WPA_AUTH_ALG_SHARED) + type = NL80211_AUTHTYPE_SHARED_KEY; + else if (params->auth_alg & WPA_AUTH_ALG_LEAP) + type = NL80211_AUTHTYPE_NETWORK_EAP; + else if (params->auth_alg & WPA_AUTH_ALG_FT) + type = NL80211_AUTHTYPE_FT; + else + goto fail; + + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + goto fail; + +skip_auth_type: + ret = nl80211_set_conn_keys(params, msg); + if (ret) + goto fail; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; - if (ret == 0) { - bss->freq = freq; - return 0; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " + "(%s)", ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Connect request send successfully"); } - wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " - "%d (%s)", freq, ret, strerror(-ret)); -nla_put_failure: + +fail: nlmsg_free(msg); - return -1; + return ret; + } -static u32 sta_flags_nl80211(int flags) +static int wpa_driver_nl80211_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) { - u32 f = 0; + int ret; - if (flags & WPA_STA_AUTHORIZED) - f |= BIT(NL80211_STA_FLAG_AUTHORIZED); - if (flags & WPA_STA_WMM) - f |= BIT(NL80211_STA_FLAG_WME); - if (flags & WPA_STA_SHORT_PREAMBLE) - f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); - if (flags & WPA_STA_MFP) - f |= BIT(NL80211_STA_FLAG_MFP); - if (flags & WPA_STA_TDLS_PEER) - f |= BIT(NL80211_STA_FLAG_TDLS_PEER); + /* Store the connection attempted bssid for future use */ + if (params->bssid) + os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); - return f; + ret = wpa_driver_nl80211_try_connect(drv, params); + if (ret == -EALREADY) { + /* + * cfg80211 does not currently accept new connections if + * we are already connected. As a workaround, force + * disconnection and try again. + */ + wpa_printf(MSG_DEBUG, "nl80211: Explicitly " + "disconnecting before reassociation " + "attempt"); + if (wpa_driver_nl80211_disconnect( + drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + return -1; + ret = wpa_driver_nl80211_try_connect(drv, params); + } + return ret; } -static int wpa_driver_nl80211_sta_add(void *priv, - struct hostapd_sta_add_params *params) +static int wpa_driver_nl80211_associate( + void *priv, struct wpa_driver_associate_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg, *wme = NULL; - struct nl80211_sta_flag_update upd; - int ret = -ENOBUFS; + int ret = -1; + struct nl_msg *msg; - if ((params->flags & WPA_STA_TDLS_PEER) && - !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) - return -EOPNOTSUPP; + nl80211_unmask_11b_rates(bss); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + if (params->mode == IEEE80211_MODE_AP) + return wpa_driver_nl80211_ap(drv, params); - nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : - NL80211_CMD_NEW_STATION); + if (params->mode == IEEE80211_MODE_IBSS) + return wpa_driver_nl80211_ibss(drv, params); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, - params->supp_rates); - if (!params->set) { - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, - params->listen_interval); - } - if (params->ht_capabilities) { - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, - sizeof(*params->ht_capabilities), - params->ht_capabilities); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + enum nl80211_iftype nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) + return -1; + return wpa_driver_nl80211_connect(drv, params); } - os_memset(&upd, 0, sizeof(upd)); - upd.mask = sta_flags_nl80211(params->flags); - upd.set = upd.mask; - NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + nl80211_mark_disconnected(drv); - if (params->flags & WPA_STA_WMM) { - wme = nlmsg_alloc(); - if (!wme) - goto nla_put_failure; + wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", + drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE); + if (!msg) + return -1; + + ret = nl80211_connect_common(drv, params, msg); + if (ret) + goto fail; - NLA_PUT_U8(wme, NL80211_STA_WME_UAPSD_QUEUES, - params->qosinfo & WMM_QOSINFO_STA_AC_MASK); - NLA_PUT_U8(wme, NL80211_STA_WME_MAX_SP, - (params->qosinfo > WMM_QOSINFO_STA_SP_SHIFT) & - WMM_QOSINFO_STA_SP_MASK); - if (nla_put_nested(msg, NL80211_ATTR_STA_WME, wme) < 0) - goto nla_put_failure; + if (params->prev_bssid) { + wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, + MAC2STR(params->prev_bssid)); + if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + params->prev_bssid)) + goto fail; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " - "result: %d (%s)", params->set ? "SET" : "NEW", ret, - strerror(-ret)); - if (ret == -EEXIST) - ret = 0; - nla_put_failure: - nlmsg_free(wme); + if (ret) { + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (assoc): ret=%d (%s)", + ret, strerror(-ret)); + nl80211_dump_scan(drv); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Association request send successfully"); + } + +fail: nlmsg_free(msg); return ret; } -static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr) +static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, + int ifindex, enum nl80211_iftype mode) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + int ret = -ENOBUFS; - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); + wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", + ifindex, mode, nl80211_iftype_str(mode)); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE); + if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode)) + goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret == -ENOENT) + msg = NULL; + if (!ret) return 0; - return ret; - nla_put_failure: +fail: nlmsg_free(msg); - return -ENOBUFS; + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" + " %d (%s)", ifindex, mode, ret, strerror(-ret)); + return ret; } -static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, - int ifidx) +static int wpa_driver_nl80211_set_mode_impl( + struct i802_bss *bss, + enum nl80211_iftype nlmode, + struct hostapd_freq_params *desired_freq_params) { - struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + int i; + int was_ap = is_ap_interface(drv->nlmode); + int res; + int mode_switch_res; - wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx); + mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (mode_switch_res && nlmode == nl80211_get_ifmode(bss)) + mode_switch_res = 0; - /* stop listening for EAPOL on this interface */ - del_ifidx(drv, ifidx); + if (mode_switch_res == 0) { + drv->nlmode = nlmode; + ret = 0; + goto done; + } - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; + if (mode_switch_res == -ENODEV) + return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + if (nlmode == drv->nlmode) { + wpa_printf(MSG_DEBUG, "nl80211: Interface already in " + "requested mode - ignore error"); + ret = 0; + goto done; /* Already in the requested mode */ + } - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return; - msg = NULL; - nla_put_failure: - nlmsg_free(msg); - wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); -} + /* mac80211 doesn't allow mode changes while the device is up, so + * take the device down, try to set the mode again, and bring the + * device back up. + */ + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + res = i802_set_iface_flags(bss, 0); + if (res == -EACCES || res == -ENODEV) + break; + if (res != 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); + continue; + } + /* + * Setting the mode will fail for some drivers if the phy is + * on a frequency that the mode is disallowed in. + */ + if (desired_freq_params) { + res = nl80211_set_channel(bss, desired_freq_params, 0); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to set frequency on interface"); + } + } -static const char * nl80211_iftype_str(enum nl80211_iftype mode) -{ - switch (mode) { - case NL80211_IFTYPE_ADHOC: - return "ADHOC"; - case NL80211_IFTYPE_STATION: - return "STATION"; - case NL80211_IFTYPE_AP: - return "AP"; - case NL80211_IFTYPE_MONITOR: - return "MONITOR"; - case NL80211_IFTYPE_P2P_CLIENT: - return "P2P_CLIENT"; - case NL80211_IFTYPE_P2P_GO: - return "P2P_GO"; - default: - return "unknown"; + /* Try to set the mode again while the interface is down */ + mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (mode_switch_res == -EBUSY) { + wpa_printf(MSG_DEBUG, + "nl80211: Delaying mode set while interface going down"); + os_sleep(0, 100000); + continue; + } + ret = mode_switch_res; + break; } -} + if (!ret) { + wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " + "interface is down"); + drv->nlmode = nlmode; + drv->ignore_if_down_event = 1; + } -static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, - const char *ifname, - enum nl80211_iftype iftype, - const u8 *addr, int wds) -{ - struct nl_msg *msg, *flags = NULL; - int ifidx; - int ret = -ENOBUFS; - - wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", - iftype, nl80211_iftype_str(iftype)); + /* Bring the interface back up */ + res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1); + if (res != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to set interface up after switching mode"); + ret = -1; + } - msg = nlmsg_alloc(); - if (!msg) - return -1; +done: + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " + "from %d failed", nlmode, drv->nlmode); + return ret; + } - nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + if (is_p2p_net_interface(nlmode)) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s mode change to P2P - disable 11b rates", + bss->ifname); + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + } else if (drv->disabled_11b_rates) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates", + bss->ifname); + nl80211_disable_11b_rates(drv, drv->ifindex, 0); + } - if (iftype == NL80211_IFTYPE_MONITOR) { - int err; + if (is_ap_interface(nlmode)) { + nl80211_mgmt_unsubscribe(bss, "start AP"); + /* Setup additional AP mode functionality if needed */ + if (nl80211_setup_ap(bss)) + return -1; + } else if (was_ap) { + /* Remove additional AP mode functionality */ + nl80211_teardown_ap(bss); + } else { + nl80211_mgmt_unsubscribe(bss, "mode change"); + } - flags = nlmsg_alloc(); - if (!flags) - goto nla_put_failure; + if (is_mesh_interface(nlmode) && + nl80211_mgmt_subscribe_mesh(bss)) + return -1; - NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + if (!bss->in_deinit && !is_ap_interface(nlmode) && + !is_mesh_interface(nlmode) && + nl80211_mgmt_subscribe_non_ap(bss) < 0) + wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " + "frame processing - ignore for now"); - err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + return 0; +} - nlmsg_free(flags); - if (err) - goto nla_put_failure; - } else if (wds) { - NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); - } +int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode) +{ + return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL); +} - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - nla_put_failure: - nlmsg_free(msg); - wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", - ifname, ret, strerror(-ret)); - return ret; - } - ifidx = if_nametoindex(ifname); - wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", - ifname, ifidx); +static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, + struct hostapd_freq_params *freq) +{ + return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC, + freq); +} - if (ifidx <= 0) - return -1; - /* start listening for EAPOL on this interface */ - add_ifidx(drv, ifidx); +static int wpa_driver_nl80211_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; - if (addr && iftype != NL80211_IFTYPE_MONITOR && - linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) { - nl80211_remove_iface(drv, ifidx); + if (!drv->has_capability) return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + if (drv->extended_capa && drv->extended_capa_mask) { + capa->extended_capa = drv->extended_capa; + capa->extended_capa_mask = drv->extended_capa_mask; + capa->extended_capa_len = drv->extended_capa_len; } - return ifidx; + return 0; } -static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, - const char *ifname, enum nl80211_iftype iftype, - const u8 *addr, int wds) +static int wpa_driver_nl80211_set_operstate(void *priv, int state) { - int ret; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; - ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds); + wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)", + bss->ifname, drv->operstate, state, + state ? "UP" : "DORMANT"); + drv->operstate = state; + return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, + state ? IF_OPER_UP : IF_OPER_DORMANT); +} - /* if error occurred and interface exists already */ - if (ret == -ENFILE && if_nametoindex(ifname)) { - wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname); - /* Try to remove the interface that was already there. */ - nl80211_remove_iface(drv, if_nametoindex(ifname)); +static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nl80211_sta_flag_update upd; + int ret; - /* Try to create the interface again */ - ret = nl80211_create_iface_once(drv, ifname, iftype, addr, - wds); + if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) { + wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated"); + return 0; } - if (ret >= 0 && is_p2p_interface(iftype)) - nl80211_disable_11b_rates(drv, ret, 1); + wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for " + MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid)); + + os_memset(&upd, 0, sizeof(upd)); + upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED); + if (authorized) + upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) || + nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)", + ret, strerror(-ret)); return ret; } -static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct i802_bss *bss = priv; + return nl80211_set_channel(bss, freq, 0); +} + + +static inline int min_int(int a, int b) { - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; + if (a < b) + return a; + return b; +} - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = buf; - event.tx_status.data_len = len; - event.tx_status.ack = ok; - wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); +static int get_key_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the key index and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending key notifications. + */ + + if (tb[NL80211_ATTR_KEY_SEQ]) + memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), + min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); + return NL_SKIP; } -static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len) +static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, + int idx, u8 *seq) { - struct ieee80211_hdr *hdr = (void *)buf; - u16 fc; - union wpa_event_data event; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; - if (len < sizeof(*hdr)) - return; + msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0, + NL80211_CMD_GET_KEY); + if (!msg || + (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || + nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) { + nlmsg_free(msg); + return -ENOBUFS; + } - fc = le_to_host16(hdr->frame_control); + memset(seq, 0, 6); - os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); - event.rx_from_unknown.addr = hdr->addr2; - event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == - (WLAN_FC_FROMDS | WLAN_FC_TODS); - wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); + return send_and_recv_msgs(drv, msg, get_key_handler, seq); } -static void handle_frame(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len, int datarate, int ssi_signal) +static int i802_set_rts(void *priv, int rts) { - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + u32 val; - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); + if (rts >= 2347) + val = (u32) -1; + else + val = rts; - switch (WLAN_FC_GET_TYPE(fc)) { - case WLAN_FC_TYPE_MGMT: - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - event.rx_mgmt.datarate = datarate; - event.rx_mgmt.ssi_signal = ssi_signal; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); - break; - case WLAN_FC_TYPE_CTRL: - /* can only get here with PS-Poll frames */ - wpa_printf(MSG_DEBUG, "CTRL"); - from_unknown_sta(drv, buf, len); - break; - case WLAN_FC_TYPE_DATA: - from_unknown_sta(drv, buf, len); - break; + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) { + nlmsg_free(msg); + return -ENOBUFS; } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " + "%d (%s)", rts, ret, strerror(-ret)); + return ret; } -static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +static int i802_set_frag(void *priv, int frag) { - struct wpa_driver_nl80211_data *drv = eloop_ctx; - int len; - unsigned char buf[3000]; - struct ieee80211_radiotap_iterator iter; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; int ret; - int datarate = 0, ssi_signal = 0; - int injected = 0, failed = 0, rxflags = 0; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } + u32 val; - if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { - printf("received invalid radiotap frame\n"); - return; - } + if (frag >= 2346) + val = (u32) -1; + else + val = frag; - while (1) { - ret = ieee80211_radiotap_iterator_next(&iter); - if (ret == -ENOENT) - break; - if (ret) { - printf("received invalid radiotap frame (%d)\n", ret); - return; - } - switch (iter.this_arg_index) { - case IEEE80211_RADIOTAP_FLAGS: - if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) - len -= 4; - break; - case IEEE80211_RADIOTAP_RX_FLAGS: - rxflags = 1; - break; - case IEEE80211_RADIOTAP_TX_FLAGS: - injected = 1; - failed = le_to_host16((*(uint16_t *) iter.this_arg)) & - IEEE80211_RADIOTAP_F_TX_FAIL; - break; - case IEEE80211_RADIOTAP_DATA_RETRIES: - break; - case IEEE80211_RADIOTAP_CHANNEL: - /* TODO: convert from freq/flags to channel number */ - break; - case IEEE80211_RADIOTAP_RATE: - datarate = *iter.this_arg * 5; - break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: - ssi_signal = (s8) *iter.this_arg; - break; - } + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) { + nlmsg_free(msg); + return -ENOBUFS; } - if (rxflags && injected) - return; - - if (!injected) - handle_frame(drv, buf + iter.max_length, - len - iter.max_length, datarate, ssi_signal); - else - handle_tx_callback(drv->ctx, buf + iter.max_length, - len - iter.max_length, !failed); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " + "%d: %d (%s)", frag, ret, strerror(-ret)); + return ret; } -/* - * we post-process the filter code later and rewrite - * this to the offset to the last instruction - */ -#define PASS 0xFF -#define FAIL 0xFE - -static struct sock_filter msock_filter_insns[] = { - /* - * do a little-endian load of the radiotap length field - */ - /* load lower byte into A */ - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), - /* put it into X (== index register) */ - BPF_STMT(BPF_MISC| BPF_TAX, 0), - /* load upper byte into A */ - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), - /* left-shift it by 8 */ - BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), - /* or with X */ - BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), - /* put result into X */ - BPF_STMT(BPF_MISC| BPF_TAX, 0), +static int i802_flush(void *priv) +{ + struct i802_bss *bss = priv; + struct nl_msg *msg; + int res; - /* - * Allow management frames through, this also gives us those - * management frames that we sent ourselves with status - */ - /* load the lower byte of the IEEE 802.11 frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off frame type and version */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), - /* accept frame if it's both 0, fall through otherwise */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), + wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)", + bss->ifname); /* - * TODO: add a bit to radiotap RX flags that indicates - * that the sending station is not associated, then - * add a filter here that filters on our DA and that flag - * to allow us to deauth frames to that bad station. - * - * For now allow all To DS data frames through. - */ - /* load the IEEE 802.11 frame control field */ - BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), - /* mask off frame type, version and DS status */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), - /* accept frame if version 0, type 2 and To DS, fall through otherwise + * XXX: FIX! this needs to flush all VLANs too */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION); + res = send_and_recv_msgs(bss->drv, msg, NULL, NULL); + if (res) { + wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " + "(%s)", res, strerror(-res)); + } + return res; +} -#if 0 - /* - * drop non-data frames - */ - /* load the lower byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off QoS bit */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), - /* drop non-data frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), -#endif - /* load the upper byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), - /* mask off toDS/fromDS */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), - /* accept WDS frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), - /* - * add header length to index - */ - /* load the lower byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off QoS bit */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), - /* right shift it by 6 to give 0 or 2 */ - BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), - /* add data frame header length */ - BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), - /* add index, was start of 802.11 header */ - BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), - /* move to index, now start of LL header */ - BPF_STMT(BPF_MISC | BPF_TAX, 0), +static int get_sta_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct hostap_sta_driver_data *data = arg; + struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, + }; - /* - * Accept empty data frames, we use those for - * polling activity. - */ - BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); /* - * Accept EAPOL frames + * TODO: validate the interface and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending station notifications. */ - BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), - BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), - - /* keep these last two statements or change the code below */ - /* return 0 == "DROP" */ - BPF_STMT(BPF_RET | BPF_K, 0), - /* return ~0 == "keep all" */ - BPF_STMT(BPF_RET | BPF_K, ~0), -}; - -static struct sock_fprog msock_filter = { - .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), - .filter = msock_filter_insns, -}; - - -static int add_monitor_filter(int s) -{ - int idx; - - /* rewrite all PASS/FAIL jump offsets */ - for (idx = 0; idx < msock_filter.len; idx++) { - struct sock_filter *insn = &msock_filter_insns[idx]; - - if (BPF_CLASS(insn->code) == BPF_JMP) { - if (insn->code == (BPF_JMP|BPF_JA)) { - if (insn->k == PASS) - insn->k = msock_filter.len - idx - 2; - else if (insn->k == FAIL) - insn->k = msock_filter.len - idx - 3; - } - - if (insn->jt == PASS) - insn->jt = msock_filter.len - idx - 2; - else if (insn->jt == FAIL) - insn->jt = msock_filter.len - idx - 3; - if (insn->jf == PASS) - insn->jf = msock_filter.len - idx - 2; - else if (insn->jf == FAIL) - insn->jf = msock_filter.len - idx - 3; - } + if (!tb[NL80211_ATTR_STA_INFO]) { + wpa_printf(MSG_DEBUG, "sta stats missing!"); + return NL_SKIP; } - - if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, - &msock_filter, sizeof(msock_filter))) { - perror("SO_ATTACH_FILTER"); - return -1; + if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], + stats_policy)) { + wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); + return NL_SKIP; } - return 0; -} - - -static void nl80211_remove_monitor_interface( - struct wpa_driver_nl80211_data *drv) -{ - drv->monitor_refcount--; - if (drv->monitor_refcount > 0) - return; + if (stats[NL80211_STA_INFO_INACTIVE_TIME]) + data->inactive_msec = + nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); + if (stats[NL80211_STA_INFO_RX_BYTES]) + data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); + if (stats[NL80211_STA_INFO_TX_BYTES]) + data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + if (stats[NL80211_STA_INFO_RX_PACKETS]) + data->rx_packets = + nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_PACKETS]) + data->tx_packets = + nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_FAILED]) + data->tx_retry_failed = + nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); - if (drv->monitor_ifidx >= 0) { - nl80211_remove_iface(drv, drv->monitor_ifidx); - drv->monitor_ifidx = -1; - } - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); - drv->monitor_sock = -1; - } + return NL_SKIP; } - -static int -nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) +static int i802_read_sta_data(struct i802_bss *bss, + struct hostap_sta_driver_data *data, + const u8 *addr) { - char buf[IFNAMSIZ]; - struct sockaddr_ll ll; - int optval; - socklen_t optlen; + struct nl_msg *msg; - if (drv->monitor_ifidx >= 0) { - drv->monitor_refcount++; - return 0; - } + os_memset(data, 0, sizeof(*data)); - if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) { - /* - * P2P interface name is of the format p2p-%s-%d. For monitor - * interface name corresponding to P2P GO, replace "p2p-" with - * "mon-" to retain the same interface name length and to - * indicate that it is a monitor interface. - */ - snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4); - } else { - /* Non-P2P interface with AP functionality. */ - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + return -ENOBUFS; } - buf[IFNAMSIZ - 1] = '\0'; + return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data); +} - drv->monitor_ifidx = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, - 0); - if (drv->monitor_ifidx == -EOPNOTSUPP) { - /* - * This is backward compatibility for a few versions of - * the kernel only that didn't advertise the right - * attributes for the only driver that then supported - * AP mode w/o monitor -- ath6kl. - */ - wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " - "monitor interface type - try to run without it"); - drv->device_ap_sme = 1; - } +static int i802_set_tx_queue_params(void *priv, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *txq, *params; - if (drv->monitor_ifidx < 0) + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY); + if (!msg) return -1; - if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1)) - goto error; - - memset(&ll, 0, sizeof(ll)); - ll.sll_family = AF_PACKET; - ll.sll_ifindex = drv->monitor_ifidx; - drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (drv->monitor_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); - goto error; - } + txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); + if (!txq) + goto fail; - if (add_monitor_filter(drv->monitor_sock)) { - wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " - "interface; do filtering in user space"); - /* This works, but will cost in performance. */ - } + /* We are only sending parameters for a single TXQ at a time */ + params = nla_nest_start(msg, 1); + if (!params) + goto fail; - if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { - perror("monitor socket bind"); - goto error; + switch (queue) { + case 0: + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO)) + goto fail; + break; + case 1: + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI)) + goto fail; + break; + case 2: + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE)) + goto fail; + break; + case 3: + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK)) + goto fail; + break; } + /* Burst time is configured in units of 0.1 msec and TXOP parameter in + * 32 usec, so need to convert the value here. */ + if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP, + (burst_time * 100 + 16) / 32) || + nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) || + nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) || + nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs)) + goto fail; - optlen = sizeof(optval); - optval = 20; - if (setsockopt - (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { - perror("Failed to set socket priority"); - goto error; - } + nla_nest_end(msg, params); - if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, - drv, NULL)) { - printf("Could not register monitor read socket\n"); - goto error; - } + nla_nest_end(msg, txq); - return 0; - error: - nl80211_remove_monitor_interface(drv); + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + msg = NULL; +fail: + nlmsg_free(msg); return -1; } -static int nl80211_setup_ap(struct i802_bss *bss) +static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr, + const char *ifname, int vlan_id) { struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; - wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d " - "use_monitor=%d", drv->device_ap_sme, drv->use_monitor); + wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR + ", ifname=%s[%d], vlan_id=%d)", + bss->ifname, if_nametoindex(bss->ifname), + MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) { + nlmsg_free(msg); + return -ENOBUFS; + } - /* - * Disable Probe Request reporting unless we need it in this way for - * devices that include the AP SME, in the other case (unless using - * monitor iface) we'll get it through the nl_mgmt socket instead. - */ - if (!drv->device_ap_sme) - wpa_driver_nl80211_probe_req_report(bss, 0); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" + MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", + MAC2STR(addr), ifname, vlan_id, ret, + strerror(-ret)); + } + return ret; +} - if (!drv->device_ap_sme && !drv->use_monitor) - if (nl80211_mgmt_subscribe_ap(bss)) - return -1; - if (drv->device_ap_sme && !drv->use_monitor) - if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) - return -1; +static int i802_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_sta_driver_data data; + int ret; - if (!drv->device_ap_sme && drv->use_monitor && - nl80211_create_monitor_interface(drv) && - !drv->device_ap_sme) + data.inactive_msec = (unsigned long) -1; + ret = i802_read_sta_data(priv, &data, addr); + if (ret == -ENOENT) + return -ENOENT; + if (ret || data.inactive_msec == (unsigned long) -1) return -1; + return data.inactive_msec / 1000; +} - if (drv->device_ap_sme && - wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " - "Probe Request frame reporting in AP mode"); - /* Try to survive without this */ - } +static int i802_sta_clear_stats(void *priv, const u8 *addr) +{ +#if 0 + /* TODO */ +#endif return 0; } -static void nl80211_teardown_ap(struct i802_bss *bss) +static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason) { + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_mgmt mgmt; - if (drv->device_ap_sme) { - wpa_driver_nl80211_probe_req_report(bss, 0); - if (!drv->use_monitor) - nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); - } else if (drv->use_monitor) - nl80211_remove_monitor_interface(drv); - else - nl80211_mgmt_unsubscribe(bss, "AP teardown"); + if (is_mesh_interface(drv->nlmode)) + return -1; - bss->beacon_set = 0; + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason); + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0, 0, 0, 0, + 0); } -static int nl80211_send_eapol_data(struct i802_bss *bss, - const u8 *addr, const u8 *data, - size_t data_len) +static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason) { - struct sockaddr_ll ll; - int ret; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_mgmt mgmt; - if (bss->drv->eapol_tx_sock < 0) { - wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL"); + if (is_mesh_interface(drv->nlmode)) return -1; - } - os_memset(&ll, 0, sizeof(ll)); - ll.sll_family = AF_PACKET; - ll.sll_ifindex = bss->ifindex; - ll.sll_protocol = htons(ETH_P_PAE); - ll.sll_halen = ETH_ALEN; - os_memcpy(ll.sll_addr, addr, ETH_ALEN); - ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0, - (struct sockaddr *) &ll, sizeof(ll)); - if (ret < 0) - wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s", - strerror(errno)); + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason); - return ret; + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, own_addr, ETH_ALEN); + memcpy(mgmt.bssid, own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc), 0, 0, 0, 0, + 0); } -static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - -static int wpa_driver_nl80211_hapd_send_eapol( - void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr, u32 flags) +static void dump_ifidx(struct wpa_driver_nl80211_data *drv) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct ieee80211_hdr *hdr; - size_t len; - u8 *pos; - int res; - int qos = flags & WPA_STA_WMM; - - if (drv->device_ap_sme || !drv->use_monitor) - return nl80211_send_eapol_data(bss, addr, data, data_len); + char buf[200], *pos, *end; + int i, res; - len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + - data_len; - hdr = os_zalloc(len); - if (hdr == NULL) { - printf("malloc() failed for i802_send_data(len=%lu)\n", - (unsigned long) len); - return -1; - } + pos = buf; + end = pos + sizeof(buf); - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); - hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); - if (encrypt) - hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); - if (qos) { - hdr->frame_control |= - host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); + for (i = 0; i < drv->num_if_indices; i++) { + if (!drv->if_indices[i]) + continue; + res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; } + *pos = '\0'; - memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); - memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); - memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s", + drv->num_if_indices, buf); +} - if (qos) { - /* Set highest priority in QoS header */ - pos[0] = 7; - pos[1] = 0; - pos += 2; - } - memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); - pos += sizeof(rfc1042_header); - WPA_PUT_BE16(pos, ETH_P_PAE); - pos += 2; - memcpy(pos, data, data_len); +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; + int *old; - res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, - 0, 0, 0, 0); - if (res < 0) { - wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " - "failed: %d (%s)", - (unsigned long) len, errno, strerror(errno)); + wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", + ifidx); + if (have_ifidx(drv, ifidx)) { + wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list", + ifidx); + return; + } + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == 0) { + drv->if_indices[i] = ifidx; + dump_ifidx(drv); + return; + } } - os_free(hdr); - return res; + if (drv->if_indices != drv->default_if_indices) + old = drv->if_indices; + else + old = NULL; + + drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, + sizeof(int)); + if (!drv->if_indices) { + if (!old) + drv->if_indices = drv->default_if_indices; + else + drv->if_indices = old; + wpa_printf(MSG_ERROR, "Failed to reallocate memory for " + "interfaces"); + wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); + return; + } else if (!old) + os_memcpy(drv->if_indices, drv->default_if_indices, + sizeof(drv->default_if_indices)); + drv->if_indices[drv->num_if_indices] = ifidx; + drv->num_if_indices++; + dump_ifidx(drv); } -static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, - int total_flags, - int flags_or, int flags_and) +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg, *flags = NULL; - struct nl80211_sta_flag_update upd; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + int i; - flags = nlmsg_alloc(); - if (!flags) { - nlmsg_free(msg); - return -ENOMEM; + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == ifidx) { + drv->if_indices[i] = 0; + break; + } } + dump_ifidx(drv); +} - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - /* - * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This - * can be removed eventually. - */ - if (total_flags & WPA_STA_AUTHORIZED) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); - if (total_flags & WPA_STA_WMM) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +{ + int i; - if (total_flags & WPA_STA_SHORT_PREAMBLE) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); + for (i = 0; i < drv->num_if_indices; i++) + if (drv->if_indices[i] == ifidx) + return 1; - if (total_flags & WPA_STA_MFP) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + return 0; +} - if (total_flags & WPA_STA_TDLS_PEER) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER); - if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) - goto nla_put_failure; +static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname, char *ifname_wds) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + char name[IFNAMSIZ + 1]; - os_memset(&upd, 0, sizeof(upd)); - upd.mask = sta_flags_nl80211(flags_or | ~flags_and); - upd.set = sta_flags_nl80211(flags_or); - NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + if (ifname_wds) + os_strlcpy(ifname_wds, name, IFNAMSIZ + 1); - nlmsg_free(flags); + wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR + " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); + if (val) { + if (!if_nametoindex(name)) { + if (nl80211_create_iface(drv, name, + NL80211_IFTYPE_AP_VLAN, + bss->addr, 1, NULL, NULL, 0) < + 0) + return -1; + if (bridge_ifname && + linux_br_add_if(drv->global->ioctl_sock, + bridge_ifname, name) < 0) + return -1; + } + if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " + "interface %s up", name); + } + return i802_set_sta_vlan(priv, addr, name, 0); + } else { + if (bridge_ifname) + linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, + name); - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(msg); - nlmsg_free(flags); - return -ENOBUFS; + i802_set_sta_vlan(priv, addr, bss->ifname, 0); + nl80211_remove_iface(drv, if_nametoindex(name)); + return 0; + } } -static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) { - enum nl80211_iftype nlmode, old_mode; - - if (params->p2p) { - wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " - "group (GO)"); - nlmode = NL80211_IFTYPE_P2P_GO; - } else - nlmode = NL80211_IFTYPE_AP; - - old_mode = drv->nlmode; - if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode)) { - nl80211_remove_monitor_interface(drv); - return -1; - } + struct wpa_driver_nl80211_data *drv = eloop_ctx; + struct sockaddr_ll lladdr; + unsigned char buf[3000]; + int len; + socklen_t fromlen = sizeof(lladdr); - if (wpa_driver_nl80211_set_freq(&drv->first_bss, params->freq, 0, 0)) { - if (old_mode != nlmode) - wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode); - nl80211_remove_monitor_interface(drv); - return -1; + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&lladdr, &fromlen); + if (len < 0) { + wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s", + strerror(errno)); + return; } - return 0; + if (have_ifidx(drv, lladdr.sll_ifindex)) + drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); } -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv) +static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, + const char *brname, const char *ifname) { - struct nl_msg *msg; - int ret = -1; + int br_ifindex; + char in_br[IFNAMSIZ]; - msg = nlmsg_alloc(); - if (!msg) - return -1; + os_strlcpy(bss->brname, brname, IFNAMSIZ); + br_ifindex = if_nametoindex(brname); + if (br_ifindex == 0) { + /* + * Bridge was configured, but the bridge device does + * not exist. Try to add it now. + */ + if (linux_br_add(drv->global->ioctl_sock, brname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the " + "bridge interface %s: %s", + brname, strerror(errno)); + return -1; + } + bss->added_bridge = 1; + br_ifindex = if_nametoindex(brname); + add_ifidx(drv, br_ifindex); + } + bss->br_ifindex = br_ifindex; - nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " - "(%s)", ret, strerror(-ret)); - goto nla_put_failure; + if (linux_br_get(in_br, ifname) == 0) { + if (os_strcmp(in_br, brname) == 0) + return 0; /* already in the bridge */ + + wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " + "bridge %s", ifname, in_br); + if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) < + 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to " + "remove interface %s from bridge " + "%s: %s", + ifname, brname, strerror(errno)); + return -1; + } } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully"); + wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", + ifname, brname); + if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " + "into bridge %s: %s", + ifname, brname, strerror(errno)); + return -1; + } + bss->added_if_into_bridge = 1; -nla_put_failure: - nlmsg_free(msg); - return ret; + return 0; } -static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) +static void *i802_init(struct hostapd_data *hapd, + struct wpa_init_params *params) { - struct nl_msg *msg; - int ret = -1; - int count = 0; + struct wpa_driver_nl80211_data *drv; + struct i802_bss *bss; + size_t i; + char brname[IFNAMSIZ]; + int ifindex, br_ifindex; + int br_added = 0; - wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); + bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, + params->global_priv, 1, + params->bssid, params->driver_params); + if (bss == NULL) + return NULL; - if (wpa_driver_nl80211_set_mode(&drv->first_bss, - NL80211_IFTYPE_ADHOC)) { - wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " - "IBSS mode"); - return -1; - } + drv = bss->drv; -retry: - msg = nlmsg_alloc(); - if (!msg) - return -1; + if (linux_br_get(brname, params->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", + params->ifname, brname); + br_ifindex = if_nametoindex(brname); + os_strlcpy(bss->brname, brname, IFNAMSIZ); + } else { + brname[0] = '\0'; + br_ifindex = 0; + } + bss->br_ifindex = br_ifindex; - nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + for (i = 0; i < params->num_bridge; i++) { + if (params->bridge[i]) { + ifindex = if_nametoindex(params->bridge[i]); + if (ifindex) + add_ifidx(drv, ifindex); + if (ifindex == br_ifindex) + br_added = 1; + } + } - if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) - goto nla_put_failure; + /* start listening for EAPOL on the default AP interface */ + add_ifidx(drv, drv->ifindex); - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); - os_memcpy(drv->ssid, params->ssid, params->ssid_len); - drv->ssid_len = params->ssid_len; + if (params->num_bridge && params->bridge[0]) { + if (i802_check_bridge(drv, bss, params->bridge[0], + params->ifname) < 0) + goto failed; + if (os_strcmp(params->bridge[0], brname) != 0) + br_added = 1; + } - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + if (!br_added && br_ifindex && + (params->num_bridge == 0 || !params->bridge[0])) + add_ifidx(drv, br_ifindex); - ret = nl80211_set_conn_keys(params, msg); - if (ret) - goto nla_put_failure; +#ifdef CONFIG_LIBNL3_ROUTE + if (bss->added_if_into_bridge) { + drv->rtnl_sk = nl_socket_alloc(); + if (drv->rtnl_sk == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock"); + goto failed; + } - if (params->bssid && params->fixed_bssid) { - wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, - MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + if (nl_connect(drv->rtnl_sk, NETLINK_ROUTE)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s", + strerror(errno)); + goto failed; + } } +#endif /* CONFIG_LIBNL3_ROUTE */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK || - params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 || - params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) { - wpa_printf(MSG_DEBUG, " * control port"); - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); + if (drv->eapol_sock < 0) { + wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s", + strerror(errno)); + goto failed; } - if (params->wpa_ie) { - wpa_hexdump(MSG_DEBUG, - " * Extra IEs for Beacon/Probe Response frames", - params->wpa_ie, params->wpa_ie_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, - params->wpa_ie); + if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) + { + wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol"); + goto failed; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)", - ret, strerror(-ret)); - count++; - if (ret == -EALREADY && count == 1) { - wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after " - "forced leave"); - nl80211_leave_ibss(drv); - nlmsg_free(msg); - goto retry; - } + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + params->own_addr)) + goto failed; + os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN); - goto nla_put_failure; - } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully"); + memcpy(bss->addr, params->own_addr, ETH_ALEN); -nla_put_failure: - nlmsg_free(msg); - return ret; + return bss; + +failed: + wpa_driver_nl80211_deinit(bss); + return NULL; } -static int wpa_driver_nl80211_try_connect( - struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) +static void i802_deinit(void *priv) { - struct nl_msg *msg; - enum nl80211_auth_type type; - int ret = 0; - int algs; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (params->bssid) { - wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, - MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); - } - if (params->freq) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); - } - if (params->bg_scan_period >= 0) { - wpa_printf(MSG_DEBUG, " * bg scan period=%d", - params->bg_scan_period); - NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, - params->bg_scan_period); - } - if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); - if (params->ssid_len > sizeof(drv->ssid)) - goto nla_put_failure; - os_memcpy(drv->ssid, params->ssid, params->ssid_len); - drv->ssid_len = params->ssid_len; - } - wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); - if (params->wpa_ie) - NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, - params->wpa_ie); - - algs = 0; - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - algs++; - if (params->auth_alg & WPA_AUTH_ALG_SHARED) - algs++; - if (params->auth_alg & WPA_AUTH_ALG_LEAP) - algs++; - if (algs > 1) { - wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " - "selection"); - goto skip_auth_type; - } + struct i802_bss *bss = priv; + wpa_driver_nl80211_deinit(bss); +} - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else - goto nla_put_failure; - wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); +static enum nl80211_iftype wpa_driver_nl80211_if_type( + enum wpa_driver_if_type type) +{ + switch (type) { + case WPA_IF_STATION: + return NL80211_IFTYPE_STATION; + case WPA_IF_P2P_CLIENT: + case WPA_IF_P2P_GROUP: + return NL80211_IFTYPE_P2P_CLIENT; + case WPA_IF_AP_VLAN: + return NL80211_IFTYPE_AP_VLAN; + case WPA_IF_AP_BSS: + return NL80211_IFTYPE_AP; + case WPA_IF_P2P_GO: + return NL80211_IFTYPE_P2P_GO; + case WPA_IF_P2P_DEVICE: + return NL80211_IFTYPE_P2P_DEVICE; + case WPA_IF_MESH: + return NL80211_IFTYPE_MESH_POINT; + } + return -1; +} -skip_auth_type: - if (params->wpa_proto) { - enum nl80211_wpa_versions ver = 0; - if (params->wpa_proto & WPA_PROTO_WPA) - ver |= NL80211_WPA_VERSION_1; - if (params->wpa_proto & WPA_PROTO_RSN) - ver |= NL80211_WPA_VERSION_2; +#if defined(CONFIG_P2P) || defined(CONFIG_MESH) - wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); - NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); +static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0) + return 1; } + return 0; +} - if (params->pairwise_suite != CIPHER_NONE) { - int cipher; - switch (params->pairwise_suite) { - case CIPHER_SMS4: - cipher = WLAN_CIPHER_SUITE_SMS4; - break; - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_GCMP: - cipher = WLAN_CIPHER_SUITE_GCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - } - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); - } +static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr) +{ + unsigned int idx; - if (params->group_suite != CIPHER_NONE) { - int cipher; + if (!drv->global) + return -1; - switch (params->group_suite) { - case CIPHER_SMS4: - cipher = WLAN_CIPHER_SUITE_SMS4; - break; - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_GCMP: - cipher = WLAN_CIPHER_SUITE_GCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; + os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN); + for (idx = 0; idx < 64; idx++) { + new_addr[0] = drv->first_bss->addr[0] | 0x02; + new_addr[0] ^= idx << 2; + if (!nl80211_addr_in_use(drv->global, new_addr)) break; - } - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); } + if (idx == 64) + return -1; - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK || - params->key_mgmt_suite == KEY_MGMT_CCKM) { - int mgmt = WLAN_AKM_SUITE_PSK; + wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address " + MACSTR, MAC2STR(new_addr)); - switch (params->key_mgmt_suite) { - case KEY_MGMT_CCKM: - mgmt = WLAN_AKM_SUITE_CCKM; - break; - case KEY_MGMT_802_1X: - mgmt = WLAN_AKM_SUITE_8021X; - break; - case KEY_MGMT_PSK: - default: - mgmt = WLAN_AKM_SUITE_PSK; - break; - } - NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); - } + return 0; +} - if (params->disable_ht) - NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); +#endif /* CONFIG_P2P || CONFIG_MESH */ - if (params->htcaps && params->htcaps_mask) { - int sz = sizeof(struct ieee80211_ht_capabilities); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, - params->htcaps_mask); - } - ret = nl80211_set_conn_keys(params, msg); - if (ret) - goto nla_put_failure; +struct wdev_info { + u64 wdev_id; + int wdev_id_set; + u8 macaddr[ETH_ALEN]; +}; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " - "(%s)", ret, strerror(-ret)); - goto nla_put_failure; +static int nl80211_wdev_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wdev_info *wi = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WDEV]) { + wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wi->wdev_id_set = 1; } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); -nla_put_failure: - nlmsg_free(msg); - return ret; + if (tb[NL80211_ATTR_MAC]) + os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + return NL_SKIP; } -static int wpa_driver_nl80211_connect( - struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) +static int wpa_driver_nl80211_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) { - int ret = wpa_driver_nl80211_try_connect(drv, params); - if (ret == -EALREADY) { - /* - * cfg80211 does not currently accept new connections if - * we are already connected. As a workaround, force - * disconnection and try again. - */ - wpa_printf(MSG_DEBUG, "nl80211: Explicitly " - "disconnecting before reassociation " - "attempt"); - if (wpa_driver_nl80211_disconnect( - drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + enum nl80211_iftype nlmode; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ifidx; + int added = 1; + + if (addr) + os_memcpy(if_addr, addr, ETH_ALEN); + nlmode = wpa_driver_nl80211_if_type(type); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + struct wdev_info p2pdev_info; + + os_memset(&p2pdev_info, 0, sizeof(p2pdev_info)); + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, nl80211_wdev_handler, + &p2pdev_info, use_existing); + if (!p2pdev_info.wdev_id_set || ifidx != 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s", + ifname); return -1; - /* Ignore the next local disconnect message. */ - drv->ignore_next_local_disconnect = 1; - ret = wpa_driver_nl80211_try_connect(drv, params); + } + + drv->global->if_add_wdevid = p2pdev_info.wdev_id; + drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set; + if (!is_zero_ether_addr(p2pdev_info.macaddr)) + os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created", + ifname, + (long long unsigned int) p2pdev_info.wdev_id); + } else { + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, NULL, NULL, use_existing); + if (use_existing && ifidx == -ENFILE) { + added = 0; + ifidx = if_nametoindex(ifname); + } else if (ifidx < 0) { + return -1; + } } - return ret; -} + if (!addr) { + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + os_memcpy(if_addr, bss->addr, ETH_ALEN); + else if (linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, if_addr) < 0) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + } -static int wpa_driver_nl80211_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - struct nl_msg *msg; +#if defined(CONFIG_P2P) || defined(CONFIG_MESH) + if (!addr && + (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || + type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) { + /* Enforce unique P2P Interface Address */ + u8 new_addr[ETH_ALEN]; - if (params->mode == IEEE80211_MODE_AP) - return wpa_driver_nl80211_ap(drv, params); + if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (nl80211_addr_in_use(drv->global, new_addr)) { + wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " + "for %s interface", type == WPA_IF_MESH ? + "mesh" : "P2P group"); + if (nl80211_vif_addr(drv, new_addr) < 0) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + } + os_memcpy(if_addr, new_addr, ETH_ALEN); + } +#endif /* CONFIG_P2P || CONFIG_MESH */ - if (params->mode == IEEE80211_MODE_IBSS) - return wpa_driver_nl80211_ibss(drv, params); + if (type == WPA_IF_AP_BSS) { + struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); + if (new_bss == NULL) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { - enum nl80211_iftype nlmode = params->p2p ? - NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + if (bridge && + i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " + "interface %s to a bridge %s", + ifname, bridge); + if (added) + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } - if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) + if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) + { + if (added) + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); + os_memcpy(new_bss->addr, if_addr, ETH_ALEN); + new_bss->ifindex = ifidx; + new_bss->drv = drv; + new_bss->next = drv->first_bss->next; + new_bss->freq = drv->first_bss->freq; + new_bss->ctx = bss_ctx; + new_bss->added_if = added; + drv->first_bss->next = new_bss; + if (drv_priv) + *drv_priv = new_bss; + nl80211_init_bss(new_bss); + + /* Subscribe management frames for this WPA_IF_AP_BSS */ + if (nl80211_setup_ap(new_bss)) return -1; - return wpa_driver_nl80211_connect(drv, params); } - drv->associated = 0; + if (drv->global) + drv->global->if_add_ifindex = ifidx; - msg = nlmsg_alloc(); - if (!msg) - return -1; + /* + * Some virtual interfaces need to process EAPOL packets and events on + * the parent interface. This is used mainly with hostapd. + */ + if (ifidx > 0 && + (drv->hostapd || + nlmode == NL80211_IFTYPE_AP_VLAN || + nlmode == NL80211_IFTYPE_WDS || + nlmode == NL80211_IFTYPE_MONITOR)) + add_ifidx(drv, ifidx); - wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", - drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); + return 0; +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (params->bssid) { - wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, - MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); - } - if (params->freq) { - wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); - drv->assoc_freq = params->freq; - } else - drv->assoc_freq = 0; - if (params->bg_scan_period >= 0) { - wpa_printf(MSG_DEBUG, " * bg scan period=%d", - params->bg_scan_period); - NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, - params->bg_scan_period); - } - if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); - if (params->ssid_len > sizeof(drv->ssid)) - goto nla_put_failure; - os_memcpy(drv->ssid, params->ssid, params->ssid_len); - drv->ssid_len = params->ssid_len; + +static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, + enum wpa_driver_if_type type, + const char *ifname) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ifindex = if_nametoindex(ifname); + + wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d", + __func__, type, ifname, ifindex, bss->added_if); + if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex)) + nl80211_remove_iface(drv, ifindex); + else if (ifindex > 0 && !bss->added_if) { + struct wpa_driver_nl80211_data *drv2; + dl_list_for_each(drv2, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) + del_ifidx(drv2, ifindex); } - wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); - if (params->wpa_ie) - NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, - params->wpa_ie); - if (params->pairwise_suite != CIPHER_NONE) { - int cipher; + if (type != WPA_IF_AP_BSS) + return 0; - switch (params->pairwise_suite) { - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_GCMP: - cipher = WLAN_CIPHER_SUITE_GCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - } - wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + } + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + bss->brname, strerror(errno)); } - if (params->group_suite != CIPHER_NONE) { - int cipher; + if (bss != drv->first_bss) { + struct i802_bss *tbss; - switch (params->group_suite) { - case CIPHER_WEP40: - cipher = WLAN_CIPHER_SUITE_WEP40; - break; - case CIPHER_WEP104: - cipher = WLAN_CIPHER_SUITE_WEP104; - break; - case CIPHER_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case CIPHER_GCMP: - cipher = WLAN_CIPHER_SUITE_GCMP; - break; - case CIPHER_TKIP: - default: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; + wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it"); + for (tbss = drv->first_bss; tbss; tbss = tbss->next) { + if (tbss->next == bss) { + tbss->next = bss->next; + /* Unsubscribe management frames */ + nl80211_teardown_ap(bss); + nl80211_destroy_bss(bss); + if (!bss->added_if) + i802_set_iface_flags(bss, 0); + os_free(bss); + bss = NULL; + break; + } + } + if (bss) + wpa_printf(MSG_INFO, "nl80211: %s - could not find " + "BSS %p in the list", __func__, bss); + } else { + wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); + nl80211_teardown_ap(bss); + if (!bss->added_if && !drv->first_bss->next) + wpa_driver_nl80211_del_beacon(drv); + nl80211_destroy_bss(bss); + if (!bss->added_if) + i802_set_iface_flags(bss, 0); + if (drv->first_bss->next) { + drv->first_bss = drv->first_bss->next; + drv->ctx = drv->first_bss->ctx; + os_free(bss); + } else { + wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to"); } - wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); } -#ifdef CONFIG_IEEE80211W - if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) - NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); -#endif /* CONFIG_IEEE80211W */ + return 0; +} - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); - if (params->prev_bssid) { - wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, - MAC2STR(params->prev_bssid)); - NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, - params->prev_bssid); - } +static int cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_COOKIE]) + *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + return NL_SKIP; +} - if (params->disable_ht) - NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); - if (params->htcaps && params->htcaps_mask) { - int sz = sizeof(struct ieee80211_ht_capabilities); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, - params->htcaps_mask); - } +static int nl80211_send_frame_cmd(struct i802_bss *bss, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, + u64 *cookie_out, int no_cck, int no_ack, + int offchanok) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u64 cookie; + int ret = -1; - if (params->p2p) - wpa_printf(MSG_DEBUG, " * P2P group"); + wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " + "no_ack=%d offchanok=%d", + freq, wait, no_cck, no_ack, offchanok); + wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len); + + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) || + (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || + (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) || + (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || + drv->test_use_roc_tx) && + nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) || + (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) || + (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) || + nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf)) + goto fail; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); msg = NULL; if (ret) { - wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: MLME command failed (assoc): ret=%d (%s)", - ret, strerror(-ret)); - nl80211_dump_scan(drv); - goto nla_put_failure; + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s) (freq=%u wait=%u)", ret, strerror(-ret), + freq, wait); + } else { + wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; " + "cookie 0x%llx", no_ack ? " (no ACK)" : "", + (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = no_ack ? (u64) -1 : cookie; } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Association request send " - "successfully"); -nla_put_failure: +fail: nlmsg_free(msg); return ret; } -static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, - int ifindex, enum nl80211_iftype mode) +static int wpa_driver_nl80211_send_action(struct i802_bss *bss, + unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) { - struct nl_msg *msg; - int ret = -ENOBUFS; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + u8 *buf; + struct ieee80211_hdr *hdr; - wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", - ifindex, mode, nl80211_iftype_str(mode)); + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " + "freq=%u MHz wait=%d ms no_cck=%d)", + drv->ifindex, freq, wait_time, no_cck); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + buf = os_zalloc(24 + data_len); + if (buf == NULL) + return ret; + os_memcpy(buf + 24, data, data_len); + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); + os_memcpy(hdr->addr1, dst, ETH_ALEN); + os_memcpy(hdr->addr2, src, ETH_ALEN); + os_memcpy(hdr->addr3, bssid, ETH_ALEN); - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); + if (is_ap_interface(drv->nlmode) && + (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || + (int) freq == bss->freq || drv->device_ap_sme || + !drv->use_monitor)) + ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, + 0, freq, no_cck, 1, + wait_time); + else + ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, + 24 + data_len, + &drv->send_action_cookie, + no_cck, 0, 1); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (!ret) - return 0; -nla_put_failure: - nlmsg_free(msg); - wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" - " %d (%s)", ifindex, mode, ret, strerror(-ret)); + os_free(buf); return ret; } -static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, - enum nl80211_iftype nlmode) +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) { + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - int i; - int was_ap = is_ap_interface(drv->nlmode); - int res; + struct nl_msg *msg; + int ret; - res = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (res == 0) { - drv->nlmode = nlmode; - ret = 0; - goto done; + wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", + (long long unsigned int) drv->send_action_cookie); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) || + nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) { + nlmsg_free(msg); + return; } - if (res == -ENODEV) - return -1; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " + "(%s)", ret, strerror(-ret)); +} - if (nlmode == drv->nlmode) { - wpa_printf(MSG_DEBUG, "nl80211: Interface already in " - "requested mode - ignore error"); - ret = 0; - goto done; /* Already in the requested mode */ - } - /* mac80211 doesn't allow mode changes while the device is up, so - * take the device down, try to set the mode again, and bring the - * device back up. - */ - wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " - "interface down"); - for (i = 0; i < 10; i++) { - res = linux_set_iface_flags(drv->global->ioctl_sock, - bss->ifname, 0); - if (res == -EACCES || res == -ENODEV) - break; - if (res == 0) { - /* Try to set the mode again while the interface is - * down */ - ret = nl80211_set_mode(drv, drv->ifindex, nlmode); - if (ret == -EACCES) - break; - res = linux_set_iface_flags(drv->global->ioctl_sock, - bss->ifname, 1); - if (res && !ret) - ret = -1; - else if (ret != -EBUSY) - break; - } else - wpa_printf(MSG_DEBUG, "nl80211: Failed to set " - "interface down"); - os_sleep(0, 100000); - } +static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + u64 cookie; - if (!ret) { - wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " - "interface is down"); - drv->nlmode = nlmode; - drv->ignore_if_down_event = 1; + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || + nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) { + nlmsg_free(msg); + return -1; } -done: - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " - "from %d failed", nlmode, drv->nlmode); - return ret; + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " + "0x%llx for freq=%u MHz duration=%u", + (long long unsigned int) cookie, freq, duration); + drv->remain_on_chan_cookie = cookie; + drv->pending_remain_on_chan = 1; + return 0; } + wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); + return -1; +} - if (is_p2p_interface(nlmode)) - nl80211_disable_11b_rates(drv, drv->ifindex, 1); - else if (drv->disabled_11b_rates) - nl80211_disable_11b_rates(drv, drv->ifindex, 0); - if (is_ap_interface(nlmode)) { - nl80211_mgmt_unsubscribe(bss, "start AP"); - /* Setup additional AP mode functionality if needed */ - if (nl80211_setup_ap(bss)) - return -1; - } else if (was_ap) { - /* Remove additional AP mode functionality */ - nl80211_teardown_ap(bss); - } else { - nl80211_mgmt_unsubscribe(bss, "mode change"); +static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + if (!drv->pending_remain_on_chan) { + wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " + "to cancel"); + return -1; } - if (!bss->in_deinit && !is_ap_interface(nlmode) && - nl80211_mgmt_subscribe_non_ap(bss) < 0) - wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " - "frame processing - ignore for now"); + wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " + "0x%llx", + (long long unsigned int) drv->remain_on_chan_cookie); - return 0; + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); + if (!msg || + nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) { + nlmsg_free(msg); + return -1; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) + return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " + "%d (%s)", ret, strerror(-ret)); + return -1; } -static int wpa_driver_nl80211_get_capa(void *priv, - struct wpa_driver_capa *capa) +static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (!drv->has_capability) - return -1; - os_memcpy(capa, &drv->capa, sizeof(*capa)); - return 0; -} + if (!report) { + if (bss->nl_preq && drv->device_ap_sme && + is_ap_interface(drv->nlmode) && !bss->in_deinit && + !bss->static_ap) { + /* + * Do not disable Probe Request reporting that was + * enabled in nl80211_setup_ap(). + */ + wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " + "Probe Request reporting nl_preq=%p while " + "in AP mode", bss->nl_preq); + } else if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + nl80211_destroy_eloop_handle(&bss->nl_preq); + } + return 0; + } + + if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " + "already on! nl_preq=%p", bss->nl_preq); + return 0; + } + + bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq"); + if (bss->nl_preq == NULL) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + + if (nl80211_register_frame(bss, bss->nl_preq, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_REQ << 4), + NULL, 0) < 0) + goto out_err; -static int wpa_driver_nl80211_set_operstate(void *priv, int state) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; + nl80211_register_eloop_read(&bss->nl_preq, + wpa_driver_nl80211_event_receive, + bss->nl_cb); - wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", - __func__, drv->operstate, state, state ? "UP" : "DORMANT"); - drv->operstate = state; - return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1, - state ? IF_OPER_UP : IF_OPER_DORMANT); + return 0; + + out_err: + nl_destroy_handles(&bss->nl_preq); + return -1; } -static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - struct nl80211_sta_flag_update upd; + struct nlattr *bands, *band; + int ret; - msg = nlmsg_alloc(); + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)", + ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" : + "no NL80211_TXRATE_LEGACY constraint"); + + msg = nl80211_ifindex_msg(drv, ifindex, 0, + NL80211_CMD_SET_TX_BITRATE_MASK); if (!msg) - return -ENOMEM; + return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); + bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); + if (!bands) + goto fail; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + /* + * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything + * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS + * rates. All 5 GHz rates are left enabled. + */ + band = nla_nest_start(msg, NL80211_BAND_2GHZ); + if (!band || + (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"))) + goto fail; + nla_nest_end(msg, band); - os_memset(&upd, 0, sizeof(upd)); - upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED); - if (authorized) - upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); - NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + nla_nest_end(msg, bands); - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " + "(%s)", ret, strerror(-ret)); + } else + drv->disabled_11b_rates = disabled; + + return ret; + +fail: nlmsg_free(msg); - return -ENOBUFS; + return -1; } -/* Set kernel driver on given frequency (MHz) */ -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) +static int wpa_driver_nl80211_deinit_ap(void *priv) { struct i802_bss *bss = priv; - return wpa_driver_nl80211_set_freq(bss, freq->freq, freq->ht_enabled, - freq->sec_channel_offset); -} + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + bss->beacon_set = 0; + + /* + * If the P2P GO interface was dynamically added, then it is + * possible that the interface change to station is not possible. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic) + return 0; + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); +} -#if defined(HOSTAPD) || defined(CONFIG_AP) -static inline int min_int(int a, int b) +static int wpa_driver_nl80211_stop_ap(void *priv) { - if (a < b) - return a; - return b; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + bss->beacon_set = 0; + return 0; } -static int get_key_handler(struct nl_msg *msg, void *arg) +static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) + return -1; /* - * TODO: validate the key index and mac address! - * Otherwise, there's a race condition as soon as - * the kernel starts sending key notifications. + * If the P2P Client interface was dynamically added, then it is + * possible that the interface change to station is not possible. */ + if (bss->if_dynamic) + return 0; - if (tb[NL80211_ATTR_KEY_SEQ]) - memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), - min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); - return NL_SKIP; + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } -static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, - int idx, u8 *seq) +static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY); - - if (addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); - - memset(seq, 0, 6); - - return send_and_recv_msgs(drv, msg, get_key_handler, seq); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + if (i802_set_iface_flags(bss, 1)) + wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event"); } -static int i802_set_rts(void *priv, int rts) +static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -ENOBUFS; - u32 val; + struct nlattr *cqm; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - if (rts >= 2347) - val = (u32) -1; - else - val = rts; + wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " + "hysteresis=%d", threshold, hysteresis); - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) || + !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) || + nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) || + nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) { + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, cqm); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (!ret) - return 0; -nla_put_failure: - nlmsg_free(msg); - wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " - "%d (%s)", rts, ret, strerror(-ret)); - return ret; + return send_and_recv_msgs(drv, msg, NULL, NULL); } -static int i802_set_frag(void *priv, int frag) +static int get_channel_width(struct nl_msg *msg, void *arg) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret = -ENOBUFS; - u32 val; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_signal_info *sig_change = arg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - if (frag >= 2346) - val = (u32) -1; - else - val = frag; + sig_change->center_frq1 = -1; + sig_change->center_frq2 = -1; + sig_change->chanwidth = CHAN_WIDTH_UNKNOWN; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val); + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + sig_change->chanwidth = convert2width( + nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + sig_change->center_frq1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + sig_change->center_frq2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (!ret) - return 0; -nla_put_failure: - nlmsg_free(msg); - wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " - "%d: %d (%s)", frag, ret, strerror(-ret)); - return ret; + return NL_SKIP; } -static int i802_flush(void *priv) +static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int res; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); - - /* - * XXX: FIX! this needs to flush all VLANs too - */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - res = send_and_recv_msgs(drv, msg, NULL, NULL); - if (res) { - wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " - "(%s)", res, strerror(-res)); - } - return res; - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + return send_and_recv_msgs(drv, msg, get_channel_width, sig); } -#endif /* HOSTAPD || CONFIG_AP */ - -static int get_sta_handler(struct nl_msg *msg, void *arg) +static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) { - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct hostap_sta_driver_data *data = arg; - struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; - static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { - [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, - [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, - [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, - [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, - [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, - [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, - }; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - /* - * TODO: validate the interface and mac address! - * Otherwise, there's a race condition as soon as - * the kernel starts sending station notifications. - */ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; - if (!tb[NL80211_ATTR_STA_INFO]) { - wpa_printf(MSG_DEBUG, "sta stats missing!"); - return NL_SKIP; - } - if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, - tb[NL80211_ATTR_STA_INFO], - stats_policy)) { - wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); - return NL_SKIP; - } + os_memset(si, 0, sizeof(*si)); + res = nl80211_get_link_signal(drv, si); + if (res != 0) + return res; - if (stats[NL80211_STA_INFO_INACTIVE_TIME]) - data->inactive_msec = - nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); - if (stats[NL80211_STA_INFO_RX_BYTES]) - data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); - if (stats[NL80211_STA_INFO_TX_BYTES]) - data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); - if (stats[NL80211_STA_INFO_RX_PACKETS]) - data->rx_packets = - nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); - if (stats[NL80211_STA_INFO_TX_PACKETS]) - data->tx_packets = - nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); - if (stats[NL80211_STA_INFO_TX_FAILED]) - data->tx_retry_failed = - nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); + res = nl80211_get_channel_width(drv, si); + if (res != 0) + return res; - return NL_SKIP; + return nl80211_get_link_noise(drv, si); } -static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, - const u8 *addr) + +static int wpa_driver_nl80211_shared_freq(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; + struct wpa_driver_nl80211_data *driver; + int freq = 0; - os_memset(data, 0, sizeof(*data)); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + /* + * If the same PHY is in connected state with some other interface, + * then retrieve the assoc freq. + */ + wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", + drv->phyname); + + dl_list_for_each(driver, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (drv == driver || + os_strcmp(drv->phyname, driver->phyname) != 0 || + !driver->associated) + continue; - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); + wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " + MACSTR, + driver->phyname, driver->first_bss->ifname, + MAC2STR(driver->first_bss->addr)); + if (is_ap_interface(driver->nlmode)) + freq = driver->first_bss->freq; + else + freq = nl80211_get_assoc_freq(driver); + wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", + drv->phyname, freq); + } - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (!freq) + wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " + "PHY (%s) in associated state", drv->phyname); - return send_and_recv_msgs(drv, msg, get_sta_handler, data); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + return freq; } -#if defined(HOSTAPD) || defined(CONFIG_AP) - -static int i802_set_tx_queue_params(void *priv, int queue, int aifs, - int cw_min, int cw_max, int burst_time) +static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, + int encrypt) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - struct nlattr *txq, *params; - - msg = nlmsg_alloc(); - if (!msg) - return -1; + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, + 0, 0, 0, 0); +} - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); +static int nl80211_set_param(void *priv, const char *param) +{ + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + if (param == NULL) + return 0; - txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); - if (!txq) - goto nla_put_failure; +#ifdef CONFIG_P2P + if (os_strstr(param, "use_p2p_group_interface=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; - /* We are only sending parameters for a single TXQ at a time */ - params = nla_nest_start(msg, 1); - if (!params) - goto nla_put_failure; + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } +#endif /* CONFIG_P2P */ - switch (queue) { - case 0: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO); - break; - case 1: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI); - break; - case 2: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE); - break; - case 3: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK); - break; + if (os_strstr(param, "use_monitor=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->use_monitor = 1; } - /* Burst time is configured in units of 0.1 msec and TXOP parameter in - * 32 usec, so need to convert the value here. */ - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); - nla_nest_end(msg, params); + if (os_strstr(param, "force_connect_cmd=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; + drv->force_connect_cmd = 1; + } - nla_nest_end(msg, txq); + if (os_strstr(param, "no_offchannel_tx=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + drv->test_use_roc_tx = 1; + } - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return 0; - msg = NULL; - nla_put_failure: - nlmsg_free(msg); - return -1; + return 0; } -static int i802_set_sta_vlan(void *priv, const u8 *addr, - const char *ifname, int vlan_id) +static void * nl80211_global_init(void) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret = -ENOBUFS; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + struct nl80211_global *global; + struct netlink_config *cfg; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + global->ioctl_sock = -1; + dl_list_init(&global->interfaces); + global->if_add_ifindex = -1; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, - if_nametoindex(ifname)); + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto err; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret < 0) { - wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" - MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", - MAC2STR(addr), ifname, vlan_id, ret, - strerror(-ret)); + cfg->ctx = global; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + global->netlink = netlink_init(cfg); + if (global->netlink == NULL) { + os_free(cfg); + goto err; } - nla_put_failure: - nlmsg_free(msg); - return ret; -} + if (wpa_driver_nl80211_init_nl_global(global) < 0) + goto err; -static int i802_get_inact_sec(void *priv, const u8 *addr) -{ - struct hostap_sta_driver_data data; - int ret; - - data.inactive_msec = (unsigned long) -1; - ret = i802_read_sta_data(priv, &data, addr); - if (ret || data.inactive_msec == (unsigned long) -1) - return -1; - return data.inactive_msec / 1000; -} + global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->ioctl_sock < 0) { + wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s", + strerror(errno)); + goto err; + } + return global; -static int i802_sta_clear_stats(void *priv, const u8 *addr) -{ -#if 0 - /* TODO */ -#endif - return 0; +err: + nl80211_global_deinit(global); + return NULL; } -static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, - int reason) +static void nl80211_global_deinit(void *priv) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct ieee80211_mgmt mgmt; + struct nl80211_global *global = priv; + if (global == NULL) + return; + if (!dl_list_empty(&global->interfaces)) { + wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " + "nl80211_global_deinit", + dl_list_len(&global->interfaces)); + } - if (drv->device_ap_sme) - return wpa_driver_nl80211_sta_remove(bss, addr); + if (global->netlink) + netlink_deinit(global->netlink); - memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DEAUTH); - memcpy(mgmt.da, addr, ETH_ALEN); - memcpy(mgmt.sa, own_addr, ETH_ALEN); - memcpy(mgmt.bssid, own_addr, ETH_ALEN); - mgmt.u.deauth.reason_code = host_to_le16(reason); - return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, - IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0); -} + nl_destroy_handles(&global->nl); + if (global->nl_event) + nl80211_destroy_eloop_handle(&global->nl_event); -static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, - int reason) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct ieee80211_mgmt mgmt; + nl_cb_put(global->nl_cb); - if (drv->device_ap_sme) - return wpa_driver_nl80211_sta_remove(bss, addr); + if (global->ioctl_sock >= 0) + close(global->ioctl_sock); - memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DISASSOC); - memcpy(mgmt.da, addr, ETH_ALEN); - memcpy(mgmt.sa, own_addr, ETH_ALEN); - memcpy(mgmt.bssid, own_addr, ETH_ALEN); - mgmt.u.disassoc.reason_code = host_to_le16(reason); - return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, - IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc), 0); + os_free(global); } -#endif /* HOSTAPD || CONFIG_AP */ -#ifdef HOSTAPD - -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static const char * nl80211_get_radio_name(void *priv) { - int i; - int *old; - - wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", - ifidx); - for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == 0) { - drv->if_indices[i] = ifidx; - return; - } - } - - if (drv->if_indices != drv->default_if_indices) - old = drv->if_indices; - else - old = NULL; - - drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, - sizeof(int)); - if (!drv->if_indices) { - if (!old) - drv->if_indices = drv->default_if_indices; - else - drv->if_indices = old; - wpa_printf(MSG_ERROR, "Failed to reallocate memory for " - "interfaces"); - wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); - return; - } else if (!old) - os_memcpy(drv->if_indices, drv->default_if_indices, - sizeof(drv->default_if_indices)); - drv->if_indices[drv->num_if_indices] = ifidx; - drv->num_if_indices++; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return drv->phyname; } -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, + const u8 *pmkid) { - int i; + struct nl_msg *msg; - for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == ifidx) { - drv->if_indices[i] = 0; - break; - } + if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || + (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) || + (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) { + nlmsg_free(msg); + return -ENOBUFS; } + + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); } -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { - int i; - - for (i = 0; i < drv->num_if_indices; i++) - if (drv->if_indices[i] == ifidx) - return 1; - - return 0; + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); } -static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, - const char *bridge_ifname) +static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - char name[IFNAMSIZ + 1]; + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); +} - os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); - wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR - " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); - if (val) { - if (!if_nametoindex(name)) { - if (nl80211_create_iface(drv, name, - NL80211_IFTYPE_AP_VLAN, - NULL, 1) < 0) - return -1; - if (bridge_ifname && - linux_br_add_if(drv->global->ioctl_sock, - bridge_ifname, name) < 0) - return -1; - } - if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " - "interface %s up", name); - } - return i802_set_sta_vlan(priv, addr, name, 0); - } else { - if (bridge_ifname) - linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, - name); - i802_set_sta_vlan(priv, addr, bss->ifname, 0); - return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN, - name); - } +static int nl80211_flush_pmkid(void *priv) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); + return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); } -static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +static void clean_survey_results(struct survey_results *survey_results) { - struct wpa_driver_nl80211_data *drv = eloop_ctx; - struct sockaddr_ll lladdr; - unsigned char buf[3000]; - int len; - socklen_t fromlen = sizeof(lladdr); + struct freq_survey *survey, *tmp; - len = recvfrom(sock, buf, sizeof(buf), 0, - (struct sockaddr *)&lladdr, &fromlen); - if (len < 0) { - perror("recv"); + if (dl_list_empty(&survey_results->survey_list)) return; - } - if (have_ifidx(drv, lladdr.sll_ifindex)) - drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } } -static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, - struct i802_bss *bss, - const char *brname, const char *ifname) +static void add_survey(struct nlattr **sinfo, u32 ifidx, + struct dl_list *survey_list) { - int ifindex; - char in_br[IFNAMSIZ]; + struct freq_survey *survey; - os_strlcpy(bss->brname, brname, IFNAMSIZ); - ifindex = if_nametoindex(brname); - if (ifindex == 0) { - /* - * Bridge was configured, but the bridge device does - * not exist. Try to add it now. - */ - if (linux_br_add(drv->global->ioctl_sock, brname) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to add the " - "bridge interface %s: %s", - brname, strerror(errno)); - return -1; - } - bss->added_bridge = 1; - add_ifidx(drv, if_nametoindex(brname)); + survey = os_zalloc(sizeof(struct freq_survey)); + if (!survey) + return; + + survey->ifidx = ifidx; + survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + survey->filled = 0; + + if (sinfo[NL80211_SURVEY_INFO_NOISE]) { + survey->nf = (int8_t) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + survey->filled |= SURVEY_HAS_NF; } - if (linux_br_get(in_br, ifname) == 0) { - if (os_strcmp(in_br, brname) == 0) - return 0; /* already in the bridge */ + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) { + survey->channel_time = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); + survey->filled |= SURVEY_HAS_CHAN_TIME; + } - wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " - "bridge %s", ifname, in_br); - if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) < - 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to " - "remove interface %s from bridge " - "%s: %s", - ifname, brname, strerror(errno)); - return -1; - } + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) { + survey->channel_time_busy = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); + survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY; } - wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s", - ifname, brname); - if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s " - "into bridge %s: %s", - ifname, brname, strerror(errno)); - return -1; + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) { + survey->channel_time_rx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_RX; } - bss->added_if_into_bridge = 1; - return 0; + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) { + survey->channel_time_tx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_TX; + } + + wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)", + survey->freq, + survey->nf, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy, + (unsigned long int) survey->channel_time_tx, + (unsigned long int) survey->channel_time_rx, + survey->filled); + + dl_list_add_tail(survey_list, &survey->list); } -static void *i802_init(struct hostapd_data *hapd, - struct wpa_init_params *params) +static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq, + unsigned int freq_filter) { - struct wpa_driver_nl80211_data *drv; - struct i802_bss *bss; - size_t i; - char brname[IFNAMSIZ]; - int ifindex, br_ifindex; - int br_added = 0; + if (!freq_filter) + return 1; - bss = wpa_driver_nl80211_init(hapd, params->ifname, - params->global_priv); - if (bss == NULL) - return NULL; + return freq_filter == surveyed_freq; +} - drv = bss->drv; - drv->nlmode = NL80211_IFTYPE_AP; - drv->eapol_sock = -1; - if (linux_br_get(brname, params->ifname) == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", - params->ifname, brname); - br_ifindex = if_nametoindex(brname); - } else { - brname[0] = '\0'; - br_ifindex = 0; - } +static int survey_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + struct survey_results *survey_results; + u32 surveyed_freq = 0; + u32 ifidx; - drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); - drv->if_indices = drv->default_if_indices; - for (i = 0; i < params->num_bridge; i++) { - if (params->bridge[i]) { - ifindex = if_nametoindex(params->bridge[i]); - if (ifindex) - add_ifidx(drv, ifindex); - if (ifindex == br_ifindex) - br_added = 1; - } - } - if (!br_added && br_ifindex && - (params->num_bridge == 0 || !params->bridge[0])) - add_ifidx(drv, br_ifindex); + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; - /* start listening for EAPOL on the default AP interface */ - add_ifidx(drv, drv->ifindex); + survey_results = (struct survey_results *) arg; - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0)) - goto failed; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - if (params->bssid) { - if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - params->bssid)) - goto failed; - } + if (!tb[NL80211_ATTR_IFINDEX]) + return NL_SKIP; - if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s " - "into AP mode", bss->ifname); - goto failed; - } + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (params->num_bridge && params->bridge[0] && - i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) - goto failed; + if (!tb[NL80211_ATTR_SURVEY_INFO]) + return NL_SKIP; - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) - goto failed; + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) + return NL_SKIP; - drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); - if (drv->eapol_sock < 0) { - perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); - goto failed; + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) { + wpa_printf(MSG_ERROR, "nl80211: Invalid survey data"); + return NL_SKIP; } - if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) - { - printf("Could not register read socket for eapol\n"); - goto failed; - } + surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - params->own_addr)) - goto failed; + if (!check_survey_ok(sinfo, surveyed_freq, + survey_results->freq_filter)) + return NL_SKIP; - memcpy(bss->addr, params->own_addr, ETH_ALEN); + if (survey_results->freq_filter && + survey_results->freq_filter != surveyed_freq) { + wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz", + surveyed_freq); + return NL_SKIP; + } - return bss; + add_survey(sinfo, ifidx, &survey_results->survey_list); -failed: - wpa_driver_nl80211_deinit(bss); - return NULL; + return NL_SKIP; } -static void i802_deinit(void *priv) +static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) { - wpa_driver_nl80211_deinit(priv); -} + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int err; + union wpa_event_data data; + struct survey_results *survey_results; -#endif /* HOSTAPD */ + os_memset(&data, 0, sizeof(data)); + survey_results = &data.survey_results; + dl_list_init(&survey_results->survey_list); -static enum nl80211_iftype wpa_driver_nl80211_if_type( - enum wpa_driver_if_type type) -{ - switch (type) { - case WPA_IF_STATION: - return NL80211_IFTYPE_STATION; - case WPA_IF_P2P_CLIENT: - case WPA_IF_P2P_GROUP: - return NL80211_IFTYPE_P2P_CLIENT; - case WPA_IF_AP_VLAN: - return NL80211_IFTYPE_AP_VLAN; - case WPA_IF_AP_BSS: - return NL80211_IFTYPE_AP; - case WPA_IF_P2P_GO: - return NL80211_IFTYPE_P2P_GO; - } - return -1; -} + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + if (!msg) + return -ENOBUFS; + if (freq) + data.survey_results.freq_filter = freq; -#ifdef CONFIG_P2P + do { + wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data"); + err = send_and_recv_msgs(drv, msg, survey_handler, + survey_results); + } while (err > 0); -static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) + if (err) + wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); + else + wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + + clean_survey_results(survey_results); + return err; +} + + +static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, + const u8 *replay_ctr) { - struct wpa_driver_nl80211_data *drv; - dl_list_for_each(drv, &global->interfaces, - struct wpa_driver_nl80211_data, list) { - if (os_memcmp(addr, drv->first_bss.addr, ETH_ALEN) == 0) - return 1; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *replay_nested; + struct nl_msg *msg; + int ret; + + if (!drv->set_rekey_offload) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload"); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) || + !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) || + nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) || + nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) || + nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, + replay_ctr)) { + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); + return; + } + + nla_nest_end(msg, replay_nested); + + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); + if (ret == -EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, + "nl80211: Driver does not support rekey offload"); + drv->set_rekey_offload = 0; } - return 0; } -static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, - u8 *new_addr) +static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, + const u8 *addr, int qos) { - unsigned int idx; + /* send data frame to poll STA and check whether + * this frame is ACKed */ + struct { + struct ieee80211_hdr hdr; + u16 qos_ctl; + } STRUCT_PACKED nulldata; + size_t size; - if (!drv->global) - return -1; + /* Send data frame to poll STA and check whether this frame is ACKed */ - os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN); - for (idx = 0; idx < 64; idx++) { - new_addr[0] = drv->first_bss.addr[0] | 0x02; - new_addr[0] ^= idx << 2; - if (!nl80211_addr_in_use(drv->global, new_addr)) - break; + os_memset(&nulldata, 0, sizeof(nulldata)); + + if (qos) { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_QOS_NULL); + size = sizeof(nulldata); + } else { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + size = sizeof(struct ieee80211_hdr); } - if (idx == 64) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " - MACSTR, MAC2STR(new_addr)); + nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - return 0; + if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0, + 0, 0) < 0) + wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " + "send poll frame"); } -#endif /* CONFIG_P2P */ - - -static int wpa_driver_nl80211_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) +static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, + int qos) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ifidx; -#ifdef HOSTAPD - struct i802_bss *new_bss = NULL; + struct nl_msg *msg; + int ret; - if (type == WPA_IF_AP_BSS) { - new_bss = os_zalloc(sizeof(*new_bss)); - if (new_bss == NULL) - return -1; + if (!drv->poll_command_supported) { + nl80211_send_null_frame(bss, own_addr, addr, qos); + return; } -#endif /* HOSTAPD */ - if (addr) - os_memcpy(if_addr, addr, ETH_ALEN); - ifidx = nl80211_create_iface(drv, ifname, - wpa_driver_nl80211_if_type(type), addr, - 0); - if (ifidx < 0) { -#ifdef HOSTAPD - os_free(new_bss); -#endif /* HOSTAPD */ - return -1; + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + return; } - if (!addr && - linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - if_addr) < 0) { - nl80211_remove_iface(drv, ifidx); - return -1; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Client probe request for " + MACSTR " failed: ret=%d (%s)", + MAC2STR(addr), ret, strerror(-ret)); } +} -#ifdef CONFIG_P2P - if (!addr && - (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || - type == WPA_IF_P2P_GO)) { - /* Enforce unique P2P Interface Address */ - u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN]; - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - own_addr) < 0 || - linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, - new_addr) < 0) { - nl80211_remove_iface(drv, ifidx); - return -1; - } - if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " - "for P2P group interface"); - if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { - nl80211_remove_iface(drv, ifidx); - return -1; - } - if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, - new_addr) < 0) { - nl80211_remove_iface(drv, ifidx); - return -1; - } - } - os_memcpy(if_addr, new_addr, ETH_ALEN); - } -#endif /* CONFIG_P2P */ +static int nl80211_set_power_save(struct i802_bss *bss, int enabled) +{ + struct nl_msg *msg; -#ifdef HOSTAPD - if (bridge && - i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " - "interface %s to a bridge %s", ifname, bridge); - nl80211_remove_iface(drv, ifidx); - os_free(new_bss); - return -1; + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) || + nla_put_u32(msg, NL80211_ATTR_PS_STATE, + enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) { + nlmsg_free(msg); + return -ENOBUFS; } + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); +} - if (type == WPA_IF_AP_BSS) { - if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) - { - nl80211_remove_iface(drv, ifidx); - os_free(new_bss); - return -1; - } - os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); - os_memcpy(new_bss->addr, if_addr, ETH_ALEN); - new_bss->ifindex = ifidx; - new_bss->drv = drv; - new_bss->next = drv->first_bss.next; - new_bss->freq = drv->first_bss.freq; - new_bss->ctx = bss_ctx; - drv->first_bss.next = new_bss; - if (drv_priv) - *drv_priv = new_bss; - nl80211_init_bss(new_bss); - /* Subscribe management frames for this WPA_IF_AP_BSS */ - if (nl80211_setup_ap(new_bss)) - return -1; +static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, + int ctwindow) +{ + struct i802_bss *bss = priv; + + wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d " + "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); + + if (opp_ps != -1 || ctwindow != -1) { +#ifdef ANDROID_P2P + wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow); +#else /* ANDROID_P2P */ + return -1; /* Not yet supported */ +#endif /* ANDROID_P2P */ } -#endif /* HOSTAPD */ - if (drv->global) - drv->global->if_add_ifindex = ifidx; + if (legacy_ps == -1) + return 0; + if (legacy_ps != 0 && legacy_ps != 1) + return -1; /* Not yet supported */ - return 0; + return nl80211_set_power_save(bss, legacy_ps); } -static int wpa_driver_nl80211_if_remove(void *priv, - enum wpa_driver_if_type type, - const char *ifname) +static int nl80211_start_radar_detection(void *priv, + struct hostapd_freq_params *freq) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ifindex = if_nametoindex(ifname); + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); - wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d", - __func__, type, ifname, ifindex); - if (ifindex <= 0) + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar " + "detection"); return -1; + } - nl80211_remove_iface(drv, ifindex); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) || + nl80211_put_freq_params(msg, freq) < 0) { + nlmsg_free(msg); + return -1; + } -#ifdef HOSTAPD - if (type != WPA_IF_AP_BSS) + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) return 0; + wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " + "%d (%s)", ret, strerror(-ret)); + return -1; +} - if (bss->added_if_into_bridge) { - if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, - bss->ifname) < 0) - wpa_printf(MSG_INFO, "nl80211: Failed to remove " - "interface %s from bridge %s: %s", - bss->ifname, bss->brname, strerror(errno)); - } - if (bss->added_bridge) { - if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) - wpa_printf(MSG_INFO, "nl80211: Failed to remove " - "bridge %s: %s", - bss->brname, strerror(errno)); - } +#ifdef CONFIG_TDLS - if (bss != &drv->first_bss) { - struct i802_bss *tbss; +static int nl80211_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) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; - for (tbss = &drv->first_bss; tbss; tbss = tbss->next) { - if (tbss->next == bss) { - tbss->next = bss->next; - /* Unsubscribe management frames */ - nl80211_teardown_ap(bss); - nl80211_destroy_bss(bss); - os_free(bss); - bss = NULL; - break; - } - } - if (bss) - wpa_printf(MSG_INFO, "nl80211: %s - could not find " - "BSS %p in the list", __func__, bss); - } -#endif /* HOSTAPD */ + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; - return 0; -} + if (!dst) + return -EINVAL; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) || + nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) || + nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) || + nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code)) + goto fail; + if (peer_capab) { + /* + * The internal enum tdls_peer_capability definition is + * currently identical with the nl80211 enum + * nl80211_tdls_peer_capability, so no conversion is needed + * here. + */ + if (nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, + peer_capab)) + goto fail; + } + if ((initiator && + nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) || + nla_put(msg, NL80211_ATTR_IE, len, buf)) + goto fail; + return send_and_recv_msgs(drv, msg, NULL, NULL); -static int cookie_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - u64 *cookie = arg; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (tb[NL80211_ATTR_COOKIE]) - *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); - return NL_SKIP; +fail: + nlmsg_free(msg); + return -ENOBUFS; } -static int nl80211_send_frame_cmd(struct i802_bss *bss, - unsigned int freq, unsigned int wait, - const u8 *buf, size_t buf_len, - u64 *cookie_out, int no_cck, int no_ack, - int offchanok) +static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) { + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - u64 cookie; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - wpa_printf(MSG_DEBUG, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " - "no_ack=%d offchanok=%d", - freq, wait, no_cck, no_ack, offchanok); - nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - if (wait) - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); - if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) - NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); - if (no_cck) - NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); - if (no_ack) - NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK); + enum nl80211_tdls_operation nl80211_oper; - NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " - "(%s) (freq=%u wait=%u)", ret, strerror(-ret), - freq, wait); - goto nla_put_failure; + switch (oper) { + case TDLS_DISCOVERY_REQ: + nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; + break; + case TDLS_SETUP: + nl80211_oper = NL80211_TDLS_SETUP; + break; + case TDLS_TEARDOWN: + nl80211_oper = NL80211_TDLS_TEARDOWN; + break; + case TDLS_ENABLE_LINK: + nl80211_oper = NL80211_TDLS_ENABLE_LINK; + break; + case TDLS_DISABLE_LINK: + nl80211_oper = NL80211_TDLS_DISABLE_LINK; + break; + case TDLS_ENABLE: + return 0; + case TDLS_DISABLE: + return 0; + default: + return -EINVAL; } - wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted%s; " - "cookie 0x%llx", no_ack ? " (no ACK)" : "", - (long long unsigned int) cookie); - if (cookie_out) - *cookie_out = no_ack ? (u64) -1 : cookie; + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) || + nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) { + nlmsg_free(msg); + return -ENOBUFS; + } -nla_put_failure: - nlmsg_free(msg); - return ret; + return send_and_recv_msgs(drv, msg, NULL, NULL); } -static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq, - unsigned int wait_time, - const u8 *dst, const u8 *src, - const u8 *bssid, - const u8 *data, size_t data_len, - int no_cck) +static int +nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - u8 *buf; - struct ieee80211_hdr *hdr; + struct nl_msg *msg; + int ret = -ENOBUFS; - wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " - "freq=%u MHz wait=%d ms no_cck=%d)", - drv->ifindex, freq, wait_time, no_cck); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) || + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; - buf = os_zalloc(24 + data_len); - if (buf == NULL) + wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR + " oper_class=%u freq=%u", + MAC2STR(addr), oper_class, params->freq); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH); + if (!msg || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) || + (ret = nl80211_put_freq_params(msg, params))) { + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch"); return ret; - os_memcpy(buf + 24, data, data_len); - hdr = (struct ieee80211_hdr *) buf; - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); - os_memcpy(hdr->addr1, dst, ETH_ALEN); - os_memcpy(hdr->addr2, src, ETH_ALEN); - os_memcpy(hdr->addr3, bssid, ETH_ALEN); - - if (is_ap_interface(drv->nlmode)) - ret = wpa_driver_nl80211_send_mlme_freq(priv, buf, - 24 + data_len, - 0, freq, no_cck, 1, - wait_time); - else - ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, - 24 + data_len, - &drv->send_action_cookie, - no_cck, 0, 1); + } - os_free(buf); - return ret; + return send_and_recv_msgs(drv, msg, NULL, NULL); } -static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +static int +nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret; - msg = nlmsg_alloc(); - if (!msg) - return; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) || + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; - nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); + wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR, + MAC2STR(addr)); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH); + if (!msg || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, + "nl80211: Could not build TDLS cancel chan switch"); + return -ENOBUFS; + } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); + return send_and_recv_msgs(drv, msg, NULL, NULL); +} - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) - wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " - "(%s)", ret, strerror(-ret)); +#endif /* CONFIG TDLS */ - nla_put_failure: - nlmsg_free(msg); + +static int driver_nl80211_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 i802_bss *bss = priv; + return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx, + set_tx, seq, seq_len, key, key_len); } -static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, - unsigned int duration) +static int driver_nl80211_scan2(void *priv, + struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret; - u64 cookie; + return wpa_driver_nl80211_scan(bss, params); +} - msg = nlmsg_alloc(); - if (!msg) - return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); +static int driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code); +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); - cookie = 0; - ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); - msg = NULL; - if (ret == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " - "0x%llx for freq=%u MHz duration=%u", - (long long unsigned int) cookie, freq, duration); - drv->remain_on_chan_cookie = cookie; - drv->pending_remain_on_chan = 1; - return 0; - } - wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " - "(freq=%d duration=%u): %d (%s)", - freq, duration, ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); - return -1; +static int driver_nl80211_authenticate(void *priv, + struct wpa_driver_auth_params *params) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_authenticate(bss, params); } -static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +static void driver_nl80211_deinit(void *priv) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret; + wpa_driver_nl80211_deinit(bss); +} - if (!drv->pending_remain_on_chan) { - wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " - "to cancel"); - return -1; - } - wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " - "0x%llx", - (long long unsigned int) drv->remain_on_chan_cookie); +static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, + const char *ifname) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_if_remove(bss, type, ifname); +} - msg = nlmsg_alloc(); - if (!msg) - return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); +static int driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len, int noack) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, + 0, 0, 0, 0); +} - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret == 0) - return 0; - wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " - "%d (%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); - return -1; +static int driver_nl80211_sta_remove(void *priv, const u8 *addr) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0); } -static int wpa_driver_nl80211_probe_req_report(void *priv, int report) +static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - - if (!report) { - if (bss->nl_preq && drv->device_ap_sme && - is_ap_interface(drv->nlmode)) { - /* - * Do not disable Probe Request reporting that was - * enabled in nl80211_setup_ap(). - */ - wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " - "Probe Request reporting nl_preq=%p while " - "in AP mode", bss->nl_preq); - } else if (bss->nl_preq) { - wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " - "reporting nl_preq=%p", bss->nl_preq); - eloop_unregister_read_sock( - nl_socket_get_fd(bss->nl_preq)); - nl_destroy_handles(&bss->nl_preq); - } - return 0; - } + return i802_set_sta_vlan(bss, addr, ifname, vlan_id); +} - if (bss->nl_preq) { - wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " - "already on! nl_preq=%p", bss->nl_preq); - return 0; - } - bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq"); - if (bss->nl_preq == NULL) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request " - "reporting nl_preq=%p", bss->nl_preq); +static int driver_nl80211_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct i802_bss *bss = priv; + return i802_read_sta_data(bss, data, addr); +} - if (nl80211_register_frame(bss, bss->nl_preq, - (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_PROBE_REQ << 4), - NULL, 0) < 0) - goto out_err; - eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq), - wpa_driver_nl80211_event_receive, bss->nl_cb, - bss->nl_preq); +static int driver_nl80211_send_action(void *priv, unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src, + bssid, data, data_len, no_cck); +} - return 0; - out_err: - nl_destroy_handles(&bss->nl_preq); - return -1; +static int driver_nl80211_probe_req_report(void *priv, int report) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_probe_req_report(bss, report); } -static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, - int ifindex, int disabled) +static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, + const u8 *ies, size_t ies_len) { - struct nl_msg *msg; - struct nlattr *bands, *band; int ret; + struct nl_msg *msg; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + u16 mdid = WPA_GET_LE16(md); - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - - bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); - if (!bands) - goto nla_put_failure; - - /* - * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything - * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS - * rates. All 5 GHz rates are left enabled. - */ - band = nla_nest_start(msg, NL80211_BAND_2GHZ); - if (!band) - goto nla_put_failure; - if (disabled) { - NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs"); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) || + nla_put(msg, NL80211_ATTR_IE, ies_len, ies) || + nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) { + nlmsg_free(msg); + return -ENOBUFS; } - nla_nest_end(msg, band); - - nla_nest_end(msg, bands); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " - "(%s)", ret, strerror(-ret)); - } else - drv->disabled_11b_rates = disabled; + wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed " + "err=%d (%s)", ret, strerror(-ret)); + } return ret; - -nla_put_failure: - nlmsg_free(msg); - return -1; } -static int wpa_driver_nl80211_deinit_ap(void *priv) +const u8 * wpa_driver_nl80211_get_macaddr(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - if (!is_ap_interface(drv->nlmode)) - return -1; - wpa_driver_nl80211_del_beacon(drv); - return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); -} + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) + return NULL; -static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) - return -1; - return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); + return bss->addr; } -static void wpa_driver_nl80211_resume(void *priv) +static const char * scan_state_str(enum scan_states scan_state) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on " - "resume event"); + switch (scan_state) { + case NO_SCAN: + return "NO_SCAN"; + case SCAN_REQUESTED: + return "SCAN_REQUESTED"; + case SCAN_STARTED: + return "SCAN_STARTED"; + case SCAN_COMPLETED: + return "SCAN_COMPLETED"; + case SCAN_ABORTED: + return "SCAN_ABORTED"; + case SCHED_SCAN_STARTED: + return "SCHED_SCAN_STARTED"; + case SCHED_SCAN_STOPPED: + return "SCHED_SCAN_STOPPED"; + case SCHED_SCAN_RESULTS: + return "SCHED_SCAN_RESULTS"; } + + return "??"; } -static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len) +static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret; - u8 *data, *pos; - size_t data_len; - const u8 *own_addr = bss->addr; - - if (action != 1) { - wpa_printf(MSG_ERROR, "nl80211: 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, 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_driver_nl80211_send_action(bss, drv->assoc_freq, 0, - drv->bssid, own_addr, drv->bssid, - data, data_len, 0); - os_free(data); + int res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, + "ifindex=%d\n" + "ifname=%s\n" + "brname=%s\n" + "addr=" MACSTR "\n" + "freq=%d\n" + "%s%s%s%s%s", + bss->ifindex, + bss->ifname, + bss->brname, + MAC2STR(bss->addr), + bss->freq, + bss->beacon_set ? "beacon_set=1\n" : "", + bss->added_if_into_bridge ? + "added_if_into_bridge=1\n" : "", + bss->added_bridge ? "added_bridge=1\n" : "", + bss->in_deinit ? "in_deinit=1\n" : "", + bss->if_dynamic ? "if_dynamic=1\n" : ""); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + if (bss->wdev_id_set) { + res = os_snprintf(pos, end - pos, "wdev_id=%llu\n", + (unsigned long long) bss->wdev_id); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + } + + res = os_snprintf(pos, end - pos, + "phyname=%s\n" + "perm_addr=" MACSTR "\n" + "drv_ifindex=%d\n" + "operstate=%d\n" + "scan_state=%s\n" + "auth_bssid=" MACSTR "\n" + "auth_attempt_bssid=" MACSTR "\n" + "bssid=" MACSTR "\n" + "prev_bssid=" MACSTR "\n" + "associated=%d\n" + "assoc_freq=%u\n" + "monitor_sock=%d\n" + "monitor_ifidx=%d\n" + "monitor_refcount=%d\n" + "last_mgmt_freq=%u\n" + "eapol_tx_sock=%d\n" + "%s%s%s%s%s%s%s%s%s%s%s%s%s", + drv->phyname, + MAC2STR(drv->perm_addr), + drv->ifindex, + drv->operstate, + scan_state_str(drv->scan_state), + MAC2STR(drv->auth_bssid), + MAC2STR(drv->auth_attempt_bssid), + MAC2STR(drv->bssid), + MAC2STR(drv->prev_bssid), + drv->associated, + drv->assoc_freq, + drv->monitor_sock, + drv->monitor_ifidx, + drv->monitor_refcount, + drv->last_mgmt_freq, + drv->eapol_tx_sock, + drv->ignore_if_down_event ? + "ignore_if_down_event=1\n" : "", + drv->scan_complete_events ? + "scan_complete_events=1\n" : "", + drv->disabled_11b_rates ? + "disabled_11b_rates=1\n" : "", + drv->pending_remain_on_chan ? + "pending_remain_on_chan=1\n" : "", + drv->in_interface_list ? "in_interface_list=1\n" : "", + drv->device_ap_sme ? "device_ap_sme=1\n" : "", + drv->poll_command_supported ? + "poll_command_supported=1\n" : "", + drv->data_tx_status ? "data_tx_status=1\n" : "", + drv->scan_for_auth ? "scan_for_auth=1\n" : "", + drv->retry_auth ? "retry_auth=1\n" : "", + drv->use_monitor ? "use_monitor=1\n" : "", + drv->ignore_next_local_disconnect ? + "ignore_next_local_disconnect=1\n" : "", + drv->ignore_next_local_deauth ? + "ignore_next_local_deauth=1\n" : ""); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + if (drv->has_capability) { + res = os_snprintf(pos, end - pos, + "capa.key_mgmt=0x%x\n" + "capa.enc=0x%x\n" + "capa.auth=0x%x\n" + "capa.flags=0x%llx\n" + "capa.rrm_flags=0x%x\n" + "capa.max_scan_ssids=%d\n" + "capa.max_sched_scan_ssids=%d\n" + "capa.sched_scan_supported=%d\n" + "capa.max_match_sets=%d\n" + "capa.max_remain_on_chan=%u\n" + "capa.max_stations=%u\n" + "capa.probe_resp_offloads=0x%x\n" + "capa.max_acl_mac_addrs=%u\n" + "capa.num_multichan_concurrent=%u\n" + "capa.mac_addr_rand_sched_scan_supported=%d\n" + "capa.mac_addr_rand_scan_supported=%d\n", + drv->capa.key_mgmt, + drv->capa.enc, + drv->capa.auth, + (unsigned long long) drv->capa.flags, + drv->capa.rrm_flags, + drv->capa.max_scan_ssids, + drv->capa.max_sched_scan_ssids, + drv->capa.sched_scan_supported, + drv->capa.max_match_sets, + drv->capa.max_remain_on_chan, + drv->capa.max_stations, + drv->capa.probe_resp_offloads, + drv->capa.max_acl_mac_addrs, + drv->capa.num_multichan_concurrent, + drv->capa.mac_addr_rand_sched_scan_supported, + drv->capa.mac_addr_rand_scan_supported); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + } + + return pos - buf; +} + + +static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) +{ + if ((settings->head && + nla_put(msg, NL80211_ATTR_BEACON_HEAD, + settings->head_len, settings->head)) || + (settings->tail && + nla_put(msg, NL80211_ATTR_BEACON_TAIL, + settings->tail_len, settings->tail)) || + (settings->beacon_ies && + nla_put(msg, NL80211_ATTR_IE, + settings->beacon_ies_len, settings->beacon_ies)) || + (settings->proberesp_ies && + nla_put(msg, NL80211_ATTR_IE_PROBE_RESP, + settings->proberesp_ies_len, settings->proberesp_ies)) || + (settings->assocresp_ies && + nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP, + settings->assocresp_ies_len, settings->assocresp_ies)) || + (settings->probe_resp && + nla_put(msg, NL80211_ATTR_PROBE_RESP, + settings->probe_resp_len, settings->probe_resp))) + return -ENOBUFS; - return ret; + return 0; } -static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) +static int nl80211_switch_channel(void *priv, struct csa_settings *settings) { + struct nl_msg *msg; struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg, *cqm = NULL; - int ret = -1; + struct nlattr *beacon_csa; + int ret = -ENOBUFS; - wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " - "hysteresis=%d", threshold, hysteresis); + wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", + settings->cs_count, settings->block_tx, + settings->freq_params.freq, settings->freq_params.bandwidth, + settings->freq_params.center_freq1, + settings->freq_params.center_freq2); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command"); + return -EOPNOTSUPP; + } + + if ((drv->nlmode != NL80211_IFTYPE_AP) && + (drv->nlmode != NL80211_IFTYPE_P2P_GO)) + return -EOPNOTSUPP; + + /* check settings validity */ + if (!settings->beacon_csa.tail || + ((settings->beacon_csa.tail_len <= + settings->counter_offset_beacon) || + (settings->beacon_csa.tail[settings->counter_offset_beacon] != + settings->cs_count))) + return -EINVAL; + + if (settings->beacon_csa.probe_resp && + ((settings->beacon_csa.probe_resp_len <= + settings->counter_offset_presp) || + (settings->beacon_csa.probe_resp[settings->counter_offset_presp] != + settings->cs_count))) + return -EINVAL; - msg = nlmsg_alloc(); - if (!msg) - return -1; + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) || + nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, + settings->cs_count) || + (ret = nl80211_put_freq_params(msg, &settings->freq_params)) || + (settings->block_tx && + nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX))) + goto error; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); + /* beacon_after params */ + ret = set_beacon_data(msg, &settings->beacon_after); + if (ret) + goto error; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + /* beacon_csa params */ + beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES); + if (!beacon_csa) + goto fail; - cqm = nlmsg_alloc(); - if (cqm == NULL) - goto nla_put_failure; + ret = set_beacon_data(msg, &settings->beacon_csa); + if (ret) + goto error; - NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold); - NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); - if (nla_put_nested(msg, NL80211_ATTR_CQM, cqm) < 0) - goto nla_put_failure; + if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, + settings->counter_offset_beacon) || + (settings->beacon_csa.probe_resp && + nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, + settings->counter_offset_presp))) + goto fail; + nla_nest_end(msg, beacon_csa); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)", + ret, strerror(-ret)); + } + return ret; -nla_put_failure: - nlmsg_free(cqm); +fail: + ret = -ENOBUFS; +error: nlmsg_free(msg); + wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request"); return ret; } -static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) +static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr, + u8 user_priority, u16 admitted_time) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int res; + struct nl_msg *msg; + int ret; - os_memset(si, 0, sizeof(*si)); - res = nl80211_get_link_signal(drv, si); - if (res != 0) - return res; + wpa_printf(MSG_DEBUG, + "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d", + tsid, admitted_time, user_priority); - return nl80211_get_link_noise(drv, si); + if (!is_sta_interface(drv->nlmode)) + return -ENOTSUP; + + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_TSID, tsid) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) || + nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)", + ret, strerror(-ret)); + return ret; } -static int wpa_driver_nl80211_shared_freq(void *priv) +static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct wpa_driver_nl80211_data *driver; - int freq = 0; + struct nl_msg *msg; + int ret; - /* - * If the same PHY is in connected state with some other interface, - * then retrieve the assoc freq. - */ - wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", - drv->phyname); + wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid); - dl_list_for_each(driver, &drv->global->interfaces, - struct wpa_driver_nl80211_data, list) { - if (drv == driver || - os_strcmp(drv->phyname, driver->phyname) != 0 || - !driver->associated) - continue; + if (!is_sta_interface(drv->nlmode)) + return -ENOTSUP; - wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " - MACSTR, - driver->phyname, driver->first_bss.ifname, - MAC2STR(driver->first_bss.addr)); - if (is_ap_interface(driver->nlmode)) - freq = driver->first_bss.freq; - else - freq = nl80211_get_assoc_freq(driver); - wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", - drv->phyname, freq); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) || + nla_put_u8(msg, NL80211_ATTR_TSID, tsid) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + return -ENOBUFS; } - if (!freq) - wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " - "PHY (%s) in associated state", drv->phyname); - - return freq; -} - - -static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, - int encrypt) -{ - struct i802_bss *bss = priv; - return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, - 0, 0, 0, 0); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)", + ret, strerror(-ret)); + return ret; } -static int nl80211_set_param(void *priv, const char *param) +#ifdef CONFIG_TESTING_OPTIONS +static int cmd_reply_handler(struct nl_msg *msg, void *arg) { - wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); - if (param == NULL) - return 0; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpabuf *buf = arg; -#ifdef CONFIG_P2P - if (os_strstr(param, "use_p2p_group_interface=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; + if (!buf) + return NL_SKIP; - wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " - "interface"); - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) { + wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply"); + return NL_SKIP; } -#endif /* CONFIG_P2P */ - return 0; + wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0)); + + return NL_SKIP; } +#endif /* CONFIG_TESTING_OPTIONS */ -static void * nl80211_global_init(void) +static int vendor_reply_handler(struct nl_msg *msg, void *arg) { - struct nl80211_global *global; - struct netlink_config *cfg; - - global = os_zalloc(sizeof(*global)); - if (global == NULL) - return NULL; - global->ioctl_sock = -1; - dl_list_init(&global->interfaces); - global->if_add_ifindex = -1; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *nl_vendor_reply, *nl; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpabuf *buf = arg; + int rem; - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - goto err; + if (!buf) + return NL_SKIP; - cfg->ctx = global; - cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; - cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; - global->netlink = netlink_init(cfg); - if (global->netlink == NULL) { - os_free(cfg); - goto err; - } + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA]; - if (wpa_driver_nl80211_init_nl_global(global) < 0) - goto err; + if (!nl_vendor_reply) + return NL_SKIP; - global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (global->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); - goto err; + if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) { + wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply"); + return NL_SKIP; } - return global; + nla_for_each_nested(nl, nl_vendor_reply, rem) { + wpabuf_put_data(buf, nla_data(nl), nla_len(nl)); + } -err: - nl80211_global_deinit(global); - return NULL; + return NL_SKIP; } -static void nl80211_global_deinit(void *priv) +static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id, + unsigned int subcmd, const u8 *data, + size_t data_len, struct wpabuf *buf) { - struct nl80211_global *global = priv; - if (global == NULL) - return; - if (!dl_list_empty(&global->interfaces)) { - wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " - "nl80211_global_deinit", - dl_list_len(&global->interfaces)); - } - - if (global->netlink) - netlink_deinit(global->netlink); + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; - nl_destroy_handles(&global->nl); +#ifdef CONFIG_TESTING_OPTIONS + if (vendor_id == 0xffffffff) { + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - if (global->nl_event) { - eloop_unregister_read_sock( - nl_socket_get_fd(global->nl_event)); - nl_destroy_handles(&global->nl_event); + nl80211_cmd(drv, msg, 0, subcmd); + if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) < + 0) + goto fail; + ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d", + ret); + return ret; } +#endif /* CONFIG_TESTING_OPTIONS */ - nl_cb_put(global->nl_cb); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) || + (data && + nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data))) + goto fail; - if (global->ioctl_sock >= 0) - close(global->ioctl_sock); + ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d", + ret); + return ret; - os_free(global); +fail: + nlmsg_free(msg); + return -ENOBUFS; } -static const char * nl80211_get_radio_name(void *priv) +static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - return drv->phyname; + struct nl_msg *msg; + int ret; + + wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map", + qos_map_set, qos_map_set_len); + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) || + nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed"); + + return ret; } -static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, - const u8 *pmkid) +static int nl80211_set_wowlan(void *priv, + const struct wowlan_triggers *triggers) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + struct nlattr *wowlan_triggers; + int ret; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan"); + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WOWLAN)) || + !(wowlan_triggers = nla_nest_start(msg, + NL80211_ATTR_WOWLAN_TRIGGERS)) || + (triggers->any && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || + (triggers->disconnect && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || + (triggers->magic_pkt && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || + (triggers->gtk_rekey_failure && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || + (triggers->eap_identity_req && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || + (triggers->four_way_handshake && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || + (triggers->rfkill_release && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) { + nlmsg_free(msg); + return -ENOBUFS; + } - nl80211_cmd(bss->drv, msg, 0, cmd); + nla_nest_end(msg, wowlan_triggers); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - if (pmkid) - NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); - if (bssid) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed"); - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + return ret; } -static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); -} + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params; + wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed); -static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) -{ - struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, - MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); -} + if (!drv->roaming_vendor_cmd_avail) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore roaming policy change since driver does not provide command for setting it"); + return -1; + } + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ROAMING) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY, + allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS : + QCA_ROAMING_NOT_ALLOWED) || + (bssid && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) { + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); -static int nl80211_flush_pmkid(void *priv) -{ - struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); - return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); + return send_and_recv_msgs(drv, msg, NULL, NULL); } -static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, - const u8 *replay_ctr) +static int nl80211_set_mac_addr(void *priv, const u8 *addr) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nlattr *replay_nested; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return; + int new_addr = addr != NULL; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + if (!addr) + addr = drv->perm_addr; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0) + return -1; - replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); - if (!replay_nested) - goto nla_put_failure; + if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0) + { + wpa_printf(MSG_DEBUG, + "nl80211: failed to set_mac_addr for %s to " MACSTR, + bss->ifname, MAC2STR(addr)); + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, + 1) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore interface UP after failed set_mac_addr"); + } + return -1; + } - NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); - NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); - NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, - replay_ctr); + wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR, + bss->ifname, MAC2STR(addr)); + drv->addr_changed = new_addr; + os_memcpy(bss->addr, addr, ETH_ALEN); - nla_nest_end(msg, replay_nested); + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0) + { + wpa_printf(MSG_DEBUG, + "nl80211: Could not restore interface UP after set_mac_addr"); + } - send_and_recv_msgs(drv, msg, NULL, NULL); - return; - nla_put_failure: - nlmsg_free(msg); + return 0; } -static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, - const u8 *addr, int qos) -{ - /* send data frame to poll STA and check whether - * this frame is ACKed */ - struct { - struct ieee80211_hdr hdr; - u16 qos_ctl; - } STRUCT_PACKED nulldata; - size_t size; +#ifdef CONFIG_MESH - /* Send data frame to poll STA and check whether this frame is ACKed */ +static int wpa_driver_nl80211_init_mesh(void *priv) +{ + if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to set interface into mesh mode"); + return -1; + } + return 0; +} - os_memset(&nulldata, 0, sizeof(nulldata)); - if (qos) { - nulldata.hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_QOS_NULL); - size = sizeof(nulldata); - } else { - nulldata.hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_NULLFUNC); - size = sizeof(struct ieee80211_hdr); +static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id, + size_t mesh_id_len) +{ + if (mesh_id) { + wpa_hexdump_ascii(MSG_DEBUG, " * Mesh ID (SSID)", + mesh_id, mesh_id_len); + return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id); } - nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); - os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); - os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - - if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0) < 0) - wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " - "send poll frame"); + return 0; } -static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, - int qos) + +static int nl80211_join_mesh(struct i802_bss *bss, + struct wpa_driver_mesh_join_params *params) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + struct nlattr *container; + int ret = -1; - if (!drv->poll_command_supported) { - nl80211_send_null_frame(bss, own_addr, addr, qos); - return; + wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH); + if (!msg || + nl80211_put_freq_params(msg, ¶ms->freq) || + nl80211_put_basic_rates(msg, params->basic_rates) || + nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) || + nl80211_put_beacon_int(msg, params->beacon_int)) + goto fail; + + wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags); + + container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP); + if (!container) + goto fail; + + if (params->ies) { + wpa_hexdump(MSG_DEBUG, " * IEs", params->ies, params->ie_len); + if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len, + params->ies)) + goto fail; } + /* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */ + if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) { + if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) || + nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH)) + goto fail; + } + if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) && + nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE)) + goto fail; + if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) && + nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM)) + goto fail; + nla_nest_end(msg, container); - msg = nlmsg_alloc(); - if (!msg) - return; + container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); + if (!container) + goto fail; + + if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && + nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0)) + goto fail; + if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) && + nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, + params->max_peer_links)) + goto fail; + + /* + * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because + * the timer could disconnect stations even in that case. + */ + if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, + params->conf.peer_link_timeout)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT"); + goto fail; + } - nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT); + nla_nest_end(msg, container); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + ret = 0; + bss->freq = params->freq.freq; + wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); - send_and_recv_msgs(drv, msg, NULL, NULL); - return; - nla_put_failure: +fail: nlmsg_free(msg); + return ret; } -static int nl80211_set_power_save(struct i802_bss *bss, int enabled) +static int +wpa_driver_nl80211_join_mesh(void *priv, + struct wpa_driver_mesh_join_params *params) { - struct nl_msg *msg; + struct i802_bss *bss = priv; + int ret, timeout; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + timeout = params->conf.peer_link_timeout; - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, - enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + /* Disable kernel inactivity timer */ + if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) + params->conf.peer_link_timeout = 0; + + ret = nl80211_join_mesh(bss, params); + if (ret == -EINVAL && params->conf.peer_link_timeout == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Mesh join retry for peer_link_timeout"); + /* + * Old kernel does not support setting + * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds + * into future from peer_link_timeout. + */ + params->conf.peer_link_timeout = timeout + 60; + ret = nl80211_join_mesh(priv, params); + } + + params->conf.peer_link_timeout = timeout; + return ret; } -static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, - int ctwindow) +static int wpa_driver_nl80211_leave_mesh(void *priv) { struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; - wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d " - "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); - - if (opp_ps != -1 || ctwindow != -1) - return -1; /* Not yet supported */ - - if (legacy_ps == -1) - return 0; - if (legacy_ps != 0 && legacy_ps != 1) - return -1; /* Not yet supported */ + wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)", + ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: mesh leave request send successfully"); + } - return nl80211_set_power_save(bss, legacy_ps); + if (wpa_driver_nl80211_set_mode(drv->first_bss, + NL80211_IFTYPE_STATION)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to set interface into station mode"); + } + return ret; } +#endif /* CONFIG_MESH */ -#ifdef CONFIG_TDLS -static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, - u8 dialog_token, u16 status_code, - const u8 *buf, size_t len) +static int wpa_driver_br_add_ip_neigh(void *priv, u8 version, + const u8 *ipaddr, int prefixlen, + const u8 *addr) { +#ifdef CONFIG_LIBNL3_ROUTE struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; + struct rtnl_neigh *rn; + struct nl_addr *nl_ipaddr = NULL; + struct nl_addr *nl_lladdr = NULL; + int family, addrsize; + int res; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) - return -EOPNOTSUPP; + if (!ipaddr || prefixlen == 0 || !addr) + return -EINVAL; - if (!dst) + if (bss->br_ifindex == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: bridge must be set before adding an ip neigh to it"); + return -1; + } + + if (!drv->rtnl_sk) { + wpa_printf(MSG_DEBUG, + "nl80211: nl_sock for NETLINK_ROUTE is not initialized"); + return -1; + } + + if (version == 4) { + family = AF_INET; + addrsize = 4; + } else if (version == 6) { + family = AF_INET6; + addrsize = 16; + } else { return -EINVAL; + } - msg = nlmsg_alloc(); - if (!msg) + rn = rtnl_neigh_alloc(); + if (rn == NULL) return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); - NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); - NLA_PUT(msg, NL80211_ATTR_IE, len, buf); + /* set the destination ip address for neigh */ + nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize); + if (nl_ipaddr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed"); + res = -ENOMEM; + goto errout; + } + nl_addr_set_prefixlen(nl_ipaddr, prefixlen); + res = rtnl_neigh_set_dst(rn, nl_ipaddr); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: neigh set destination addr failed"); + goto errout; + } - return send_and_recv_msgs(drv, msg, NULL, NULL); + /* set the corresponding lladdr for neigh */ + nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN); + if (nl_lladdr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed"); + res = -ENOMEM; + goto errout; + } + rtnl_neigh_set_lladdr(rn, nl_lladdr); -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + rtnl_neigh_set_ifindex(rn, bss->br_ifindex); + rtnl_neigh_set_state(rn, NUD_PERMANENT); + + res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Adding bridge ip neigh failed: %s", + strerror(errno)); + } +errout: + if (nl_lladdr) + nl_addr_put(nl_lladdr); + if (nl_ipaddr) + nl_addr_put(nl_ipaddr); + if (rn) + rtnl_neigh_put(rn); + return res; +#else /* CONFIG_LIBNL3_ROUTE */ + return -1; +#endif /* CONFIG_LIBNL3_ROUTE */ } -static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) +static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version, + const u8 *ipaddr) { +#ifdef CONFIG_LIBNL3_ROUTE struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - enum nl80211_tdls_operation nl80211_oper; + struct rtnl_neigh *rn; + struct nl_addr *nl_ipaddr; + int family, addrsize; + int res; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) - return -EOPNOTSUPP; + if (!ipaddr) + return -EINVAL; - switch (oper) { - case TDLS_DISCOVERY_REQ: - nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; - break; - case TDLS_SETUP: - nl80211_oper = NL80211_TDLS_SETUP; - break; - case TDLS_TEARDOWN: - nl80211_oper = NL80211_TDLS_TEARDOWN; - break; - case TDLS_ENABLE_LINK: - nl80211_oper = NL80211_TDLS_ENABLE_LINK; - break; - case TDLS_DISABLE_LINK: - nl80211_oper = NL80211_TDLS_DISABLE_LINK; - break; - case TDLS_ENABLE: - return 0; - case TDLS_DISABLE: - return 0; - default: + if (version == 4) { + family = AF_INET; + addrsize = 4; + } else if (version == 6) { + family = AF_INET6; + addrsize = 16; + } else { return -EINVAL; } - msg = nlmsg_alloc(); - if (!msg) + if (bss->br_ifindex == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: bridge must be set to delete an ip neigh"); + return -1; + } + + if (!drv->rtnl_sk) { + wpa_printf(MSG_DEBUG, + "nl80211: nl_sock for NETLINK_ROUTE is not initialized"); + return -1; + } + + rn = rtnl_neigh_alloc(); + if (rn == NULL) return -ENOMEM; - nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + /* set the destination ip address for neigh */ + nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize); + if (nl_ipaddr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed"); + res = -ENOMEM; + goto errout; + } + res = rtnl_neigh_set_dst(rn, nl_ipaddr); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: neigh set destination addr failed"); + goto errout; + } - return send_and_recv_msgs(drv, msg, NULL, NULL); + rtnl_neigh_set_ifindex(rn, bss->br_ifindex); -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Deleting bridge ip neigh failed: %s", + strerror(errno)); + } +errout: + if (nl_ipaddr) + nl_addr_put(nl_ipaddr); + if (rn) + rtnl_neigh_put(rn); + return res; +#else /* CONFIG_LIBNL3_ROUTE */ + return -1; +#endif /* CONFIG_LIBNL3_ROUTE */ } -#endif /* CONFIG TDLS */ +static int linux_write_system_file(const char *path, unsigned int val) +{ + char buf[50]; + int fd, len; -#ifdef ANDROID + len = os_snprintf(buf, sizeof(buf), "%u\n", val); + if (os_snprintf_error(sizeof(buf), len)) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + if (write(fd, buf, len) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to write Linux system file: %s with the value of %d", + path, val); + close(fd); + return -1; + } + close(fd); -typedef struct android_wifi_priv_cmd { - char *buf; - int used_len; - int total_len; -} android_wifi_priv_cmd; + return 0; +} -static int drv_errors = 0; -static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +static const char * drv_br_port_attr_str(enum drv_br_port_attr attr) { - drv_errors++; - if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { - drv_errors = 0; - wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + switch (attr) { + case DRV_BR_PORT_ATTR_PROXYARP: + return "proxyarp_wifi"; + case DRV_BR_PORT_ATTR_HAIRPIN_MODE: + return "hairpin_mode"; } + + return NULL; } -static int android_priv_cmd(struct i802_bss *bss, const char *cmd) +static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr, + unsigned int val) { - struct wpa_driver_nl80211_data *drv = bss->drv; - struct ifreq ifr; - android_wifi_priv_cmd priv_cmd; - char buf[MAX_DRV_CMD_SIZE]; - int ret; + struct i802_bss *bss = priv; + char path[128]; + const char *attr_txt; + + attr_txt = drv_br_port_attr_str(attr); + if (attr_txt == NULL) + return -EINVAL; - os_memset(&ifr, 0, sizeof(ifr)); - os_memset(&priv_cmd, 0, sizeof(priv_cmd)); - os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s", + bss->ifname, attr_txt); - os_memset(buf, 0, sizeof(buf)); - os_strlcpy(buf, cmd, sizeof(buf)); + if (linux_write_system_file(path, val)) + return -1; - priv_cmd.buf = buf; - priv_cmd.used_len = sizeof(buf); - priv_cmd.total_len = sizeof(buf); - ifr.ifr_data = &priv_cmd; + return 0; +} - ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); - if (ret < 0) { - wpa_printf(MSG_ERROR, "%s: failed to issue private commands", - __func__); - wpa_driver_send_hang_msg(drv); - return ret; + +static const char * drv_br_net_param_str(enum drv_br_net_param param) +{ + switch (param) { + case DRV_BR_NET_PARAM_GARP_ACCEPT: + return "arp_accept"; } - drv_errors = 0; - return 0; + return NULL; } -static int android_pno_start(struct i802_bss *bss, - struct wpa_driver_scan_params *params) +static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, + unsigned int val) { - struct wpa_driver_nl80211_data *drv = bss->drv; - struct ifreq ifr; - android_wifi_priv_cmd priv_cmd; - int ret = 0, i = 0, bp; - char buf[WEXT_PNO_MAX_COMMAND_SIZE]; - - bp = WEXT_PNOSETUP_HEADER_SIZE; - os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); - buf[bp++] = WEXT_PNO_TLV_PREFIX; - buf[bp++] = WEXT_PNO_TLV_VERSION; - buf[bp++] = WEXT_PNO_TLV_SUBVERSION; - buf[bp++] = WEXT_PNO_TLV_RESERVED; - - while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { - /* Check that there is enough space needed for 1 more SSID, the - * other sections and null termination */ - if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + - WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) - break; - wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - buf[bp++] = WEXT_PNO_SSID_SECTION; - buf[bp++] = params->ssids[i].ssid_len; - os_memcpy(&buf[bp], params->ssids[i].ssid, - params->ssids[i].ssid_len); - bp += params->ssids[i].ssid_len; - i++; - } - - buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", - WEXT_PNO_SCAN_INTERVAL); - bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; - - buf[bp++] = WEXT_PNO_REPEAT_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", - WEXT_PNO_REPEAT); - bp += WEXT_PNO_REPEAT_LENGTH; - - buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", - WEXT_PNO_MAX_REPEAT); - bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; - - memset(&ifr, 0, sizeof(ifr)); - memset(&priv_cmd, 0, sizeof(priv_cmd)); - os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); - - priv_cmd.buf = buf; - priv_cmd.used_len = bp; - priv_cmd.total_len = bp; - ifr.ifr_data = &priv_cmd; - - ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + struct i802_bss *bss = priv; + char path[128]; + const char *param_txt; + int ip_version = 4; - if (ret < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", - ret); - wpa_driver_send_hang_msg(drv); - return ret; + param_txt = drv_br_net_param_str(param); + if (param_txt == NULL) + return -EINVAL; + + switch (param) { + case DRV_BR_NET_PARAM_GARP_ACCEPT: + ip_version = 4; + break; + default: + return -EINVAL; } - drv_errors = 0; + os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s", + ip_version, bss->brname, param_txt); - return android_priv_cmd(bss, "PNOFORCE 1"); + if (linux_write_system_file(path, val)) + return -1; + + return 0; } -static int android_pno_stop(struct i802_bss *bss) +static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode) { - return android_priv_cmd(bss, "PNOFORCE 0"); + switch (hw_mode) { + case HOSTAPD_MODE_IEEE80211B: + return QCA_ACS_MODE_IEEE80211B; + case HOSTAPD_MODE_IEEE80211G: + return QCA_ACS_MODE_IEEE80211G; + case HOSTAPD_MODE_IEEE80211A: + return QCA_ACS_MODE_IEEE80211A; + case HOSTAPD_MODE_IEEE80211AD: + return QCA_ACS_MODE_IEEE80211AD; + default: + return -1; + } } -#endif /* ANDROID */ + +static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *data; + int ret; + int mode; + + mode = hw_mode_to_qca_acs(params->hw_mode); + if (mode < 0) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_DO_ACS) || + !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) || + (params->ht_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) || + (params->ht40_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) { + nlmsg_free(msg); + return -ENOBUFS; + } + nla_nest_end(msg, data); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to invoke driver ACS function: %s", + strerror(errno)); + } + return ret; +} const struct wpa_driver_ops wpa_driver_nl80211_ops = { @@ -9240,37 +8436,36 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .desc = "Linux nl80211/cfg80211", .get_bssid = wpa_driver_nl80211_get_bssid, .get_ssid = wpa_driver_nl80211_get_ssid, - .set_key = wpa_driver_nl80211_set_key, - .scan2 = wpa_driver_nl80211_scan, + .set_key = driver_nl80211_set_key, + .scan2 = driver_nl80211_scan2, .sched_scan = wpa_driver_nl80211_sched_scan, .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, - .deauthenticate = wpa_driver_nl80211_deauthenticate, - .authenticate = wpa_driver_nl80211_authenticate, + .deauthenticate = driver_nl80211_deauthenticate, + .authenticate = driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, .global_init = nl80211_global_init, .global_deinit = nl80211_global_deinit, .init2 = wpa_driver_nl80211_init, - .deinit = wpa_driver_nl80211_deinit, + .deinit = driver_nl80211_deinit, .get_capa = wpa_driver_nl80211_get_capa, .set_operstate = wpa_driver_nl80211_set_operstate, .set_supp_port = wpa_driver_nl80211_set_supp_port, .set_country = wpa_driver_nl80211_set_country, + .get_country = wpa_driver_nl80211_get_country, .set_ap = wpa_driver_nl80211_set_ap, + .set_acl = wpa_driver_nl80211_set_acl, .if_add = wpa_driver_nl80211_if_add, - .if_remove = wpa_driver_nl80211_if_remove, - .send_mlme = wpa_driver_nl80211_send_mlme, - .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, + .if_remove = driver_nl80211_if_remove, + .send_mlme = driver_nl80211_send_mlme, + .get_hw_feature_data = nl80211_get_hw_feature_data, .sta_add = wpa_driver_nl80211_sta_add, - .sta_remove = wpa_driver_nl80211_sta_remove, + .sta_remove = driver_nl80211_sta_remove, .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, .sta_set_flags = wpa_driver_nl80211_sta_set_flags, -#ifdef HOSTAPD .hapd_init = i802_init, .hapd_deinit = i802_deinit, .set_wds_sta = i802_set_wds_sta, -#endif /* HOSTAPD */ -#if defined(HOSTAPD) || defined(CONFIG_AP) .get_seqnum = i802_get_seqnum, .flush = i802_flush, .get_inact_sec = i802_get_inact_sec, @@ -9278,22 +8473,20 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_rts = i802_set_rts, .set_frag = i802_set_frag, .set_tx_queue_params = i802_set_tx_queue_params, - .set_sta_vlan = i802_set_sta_vlan, + .set_sta_vlan = driver_nl80211_set_sta_vlan, .sta_deauth = i802_sta_deauth, .sta_disassoc = i802_sta_disassoc, -#endif /* HOSTAPD || CONFIG_AP */ - .read_sta_data = i802_read_sta_data, + .read_sta_data = driver_nl80211_read_sta_data, .set_freq = i802_set_freq, - .send_action = wpa_driver_nl80211_send_action, + .send_action = driver_nl80211_send_action, .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait, .remain_on_channel = wpa_driver_nl80211_remain_on_channel, .cancel_remain_on_channel = wpa_driver_nl80211_cancel_remain_on_channel, - .probe_req_report = wpa_driver_nl80211_probe_req_report, + .probe_req_report = driver_nl80211_probe_req_report, .deinit_ap = wpa_driver_nl80211_deinit_ap, .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, - .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, @@ -9306,8 +8499,42 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_rekey_info = nl80211_set_rekey_info, .poll_client = nl80211_poll_client, .set_p2p_powersave = nl80211_set_p2p_powersave, + .start_dfs_cac = nl80211_start_radar_detection, + .stop_ap = wpa_driver_nl80211_stop_ap, #ifdef CONFIG_TDLS .send_tdls_mgmt = nl80211_send_tdls_mgmt, .tdls_oper = nl80211_tdls_oper, + .tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch, + .tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch, #endif /* CONFIG_TDLS */ + .update_ft_ies = wpa_driver_nl80211_update_ft_ies, + .get_mac_addr = wpa_driver_nl80211_get_macaddr, + .get_survey = wpa_driver_nl80211_get_survey, + .status = wpa_driver_nl80211_status, + .switch_channel = nl80211_switch_channel, +#ifdef ANDROID_P2P + .set_noa = wpa_driver_set_p2p_noa, + .get_noa = wpa_driver_get_p2p_noa, + .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, +#endif /* ANDROID_P2P */ +#ifdef ANDROID + .driver_cmd = wpa_driver_nl80211_driver_cmd, +#endif /* ANDROID */ + .vendor_cmd = nl80211_vendor_cmd, + .set_qos_map = nl80211_set_qos_map, + .set_wowlan = nl80211_set_wowlan, + .roaming = nl80211_roaming, + .set_mac_addr = nl80211_set_mac_addr, +#ifdef CONFIG_MESH + .init_mesh = wpa_driver_nl80211_init_mesh, + .join_mesh = wpa_driver_nl80211_join_mesh, + .leave_mesh = wpa_driver_nl80211_leave_mesh, +#endif /* CONFIG_MESH */ + .br_add_ip_neigh = wpa_driver_br_add_ip_neigh, + .br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh, + .br_port_set_attr = wpa_driver_br_port_set_attr, + .br_set_net_param = wpa_driver_br_set_net_param, + .add_tx_ts = nl80211_add_ts, + .del_tx_ts = nl80211_del_ts, + .do_acs = wpa_driver_do_acs, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h new file mode 100644 index 000000000000..802589aa7581 --- /dev/null +++ b/src/drivers/driver_nl80211.h @@ -0,0 +1,274 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - definitions + * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg + * 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[32]; + 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; + + 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[32]; + 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_genl_ctrl_resolve(struct nl_handle *handle, const char *name); +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); + +#endif /* DRIVER_NL80211_H */ diff --git a/src/drivers/driver_nl80211_android.c b/src/drivers/driver_nl80211_android.c new file mode 100644 index 000000000000..3cc9a65867f6 --- /dev/null +++ b/src/drivers/driver_nl80211_android.c @@ -0,0 +1,220 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Android specific + * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2007, Johannes Berg + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include +#include +#include +#include +#include + +#include "utils/common.h" +#include "driver_nl80211.h" +#include "android_drv.h" + + +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +static int drv_errors = 0; + +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +{ + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } +} + + +static int android_priv_cmd(struct i802_bss *bss, const char *cmd) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + char buf[MAX_DRV_CMD_SIZE]; + int ret; + + os_memset(&ifr, 0, sizeof(ifr)); + os_memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + priv_cmd.buf = buf; + priv_cmd.used_len = sizeof(buf); + priv_cmd.total_len = sizeof(buf); + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private commands", + __func__); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + return 0; +} + + +int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { + /* Check that there is enough space needed for 1 more SSID, the + * other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) + break; + wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, + params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + i++; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + priv_cmd.buf = buf; + priv_cmd.used_len = bp; + priv_cmd.total_len = bp; + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + + return android_priv_cmd(bss, "PNOFORCE 1"); +} + + +int android_pno_stop(struct i802_bss *bss) +{ + return android_priv_cmd(bss, "PNOFORCE 0"); +} + + +#ifdef ANDROID_P2P +#ifdef ANDROID_P2P_STUB + +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) +{ + return 0; +} + + +int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) +{ + return 0; +} + + +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) +{ + return -1; +} + + +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + return 0; +} + +#endif /* ANDROID_P2P_STUB */ +#endif /* ANDROID_P2P */ + + +int android_nl_socket_set_nonblocking(struct nl_handle *handle) +{ + return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); +} + + +int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name) +{ + /* + * Android ICS has very minimal genl_ctrl_resolve() implementation, so + * need to work around that. + */ + struct nl_cache *cache = NULL; + struct genl_family *nl80211 = NULL; + int id = -1; + + if (genl_ctrl_alloc_cache(handle, &cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto fail; + } + + nl80211 = genl_ctrl_search_by_name(cache, name); + if (nl80211 == NULL) + goto fail; + + id = genl_family_get_id(nl80211); + +fail: + if (nl80211) + genl_family_put(nl80211); + if (cache) + nl_cache_free(cache); + + return id; +} diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c new file mode 100644 index 000000000000..e0d1d233e2ad --- /dev/null +++ b/src/drivers/driver_nl80211_capa.c @@ -0,0 +1,1532 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Capabilities + * Copyright (c) 2002-2015, Jouni Malinen + * Copyright (c) 2007, Johannes Berg + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/qca-vendor.h" +#include "common/qca-vendor-attr.h" +#include "driver_nl80211.h" + + +static int protocol_feature_handler(struct nl_msg *msg, void *arg) +{ + u32 *feat = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) + *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); + + return NL_SKIP; +} + + +static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) +{ + u32 feat = 0; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return 0; + + if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) { + nlmsg_free(msg); + return 0; + } + + if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0) + return feat; + + return 0; +} + + +struct wiphy_info_data { + struct wpa_driver_nl80211_data *drv; + struct wpa_driver_capa *capa; + + unsigned int num_multichan_concurrent; + + unsigned int error:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int monitor_supported:1; + unsigned int auth_supported:1; + unsigned int connect_supported:1; + unsigned int p2p_go_supported:1; + unsigned int p2p_client_supported:1; + unsigned int p2p_go_ctwindow_supported:1; + unsigned int p2p_concurrent:1; + unsigned int channel_switch_supported:1; + unsigned int set_qos_map_supported:1; + unsigned int have_low_prio_scan:1; + unsigned int wmm_ac_supported:1; + unsigned int mac_addr_rand_scan_supported:1; + unsigned int mac_addr_rand_sched_scan_supported:1; +}; + + +static unsigned int probe_resp_offload_support(int supp_protocols) +{ + unsigned int prot = 0; + + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; + + return prot; +} + + +static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_mode; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_mode, tb, i) { + switch (nla_type(nl_mode)) { + case NL80211_IFTYPE_AP: + info->capa->flags |= WPA_DRIVER_FLAGS_AP; + break; + case NL80211_IFTYPE_MESH_POINT: + info->capa->flags |= WPA_DRIVER_FLAGS_MESH; + break; + case NL80211_IFTYPE_ADHOC: + info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; + break; + case NL80211_IFTYPE_P2P_DEVICE: + info->capa->flags |= + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + break; + case NL80211_IFTYPE_P2P_GO: + info->p2p_go_supported = 1; + break; + case NL80211_IFTYPE_P2P_CLIENT: + info->p2p_client_supported = 1; + break; + case NL80211_IFTYPE_MONITOR: + info->monitor_supported = 1; + break; + } + } +} + + +static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, + struct nlattr *nl_combi) +{ + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit, *nl_mode; + int err, rem_limit, rem_mode; + int combination_has_p2p = 0, combination_has_mgd = 0; + static struct nla_policy + iface_combination_policy[NUM_NL80211_IFACE_COMB] = { + [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, + [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, + [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, + }, + iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { + [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, + [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, + }; + + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nl_combi, iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) + return 0; /* broken combination */ + + if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) + info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; + + nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], + rem_limit) { + err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, + nl_limit, iface_limit_policy); + if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) + return 0; /* broken combination */ + + nla_for_each_nested(nl_mode, + tb_limit[NL80211_IFACE_LIMIT_TYPES], + rem_mode) { + int ift = nla_type(nl_mode); + if (ift == NL80211_IFTYPE_P2P_GO || + ift == NL80211_IFTYPE_P2P_CLIENT) + combination_has_p2p = 1; + if (ift == NL80211_IFTYPE_STATION) + combination_has_mgd = 1; + } + if (combination_has_p2p && combination_has_mgd) + break; + } + + if (combination_has_p2p && combination_has_mgd) { + unsigned int num_channels = + nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); + + info->p2p_concurrent = 1; + if (info->num_multichan_concurrent < num_channels) + info->num_multichan_concurrent = num_channels; + } + + return 0; +} + + +static void wiphy_info_iface_comb(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_combi; + int rem_combi; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_combi, tb, rem_combi) { + if (wiphy_info_iface_comb_process(info, nl_combi) > 0) + break; + } +} + + +static void wiphy_info_supp_cmds(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_cmd; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_cmd, tb, i) { + switch (nla_get_u32(nl_cmd)) { + case NL80211_CMD_AUTHENTICATE: + info->auth_supported = 1; + break; + case NL80211_CMD_CONNECT: + info->connect_supported = 1; + break; + case NL80211_CMD_START_SCHED_SCAN: + info->capa->sched_scan_supported = 1; + break; + case NL80211_CMD_PROBE_CLIENT: + info->poll_command_supported = 1; + break; + case NL80211_CMD_CHANNEL_SWITCH: + info->channel_switch_supported = 1; + break; + case NL80211_CMD_SET_QOS_MAP: + info->set_qos_map_supported = 1; + break; + } + } +} + + +static void wiphy_info_cipher_suites(struct wiphy_info_data *info, + struct nlattr *tb) +{ + int i, num; + u32 *ciphers; + + if (tb == NULL) + return; + + num = nla_len(tb) / sizeof(u32); + ciphers = nla_data(tb); + for (i = 0; i < num; i++) { + u32 c = ciphers[i]; + + wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", + c >> 24, (c >> 16) & 0xff, + (c >> 8) & 0xff, c & 0xff); + switch (c) { + case WLAN_CIPHER_SUITE_CCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; + break; + case WLAN_CIPHER_SUITE_CCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; + break; + case WLAN_CIPHER_SUITE_TKIP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case WLAN_CIPHER_SUITE_WEP104: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case WLAN_CIPHER_SUITE_WEP40: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; + break; + case WLAN_CIPHER_SUITE_NO_GROUP_ADDR: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED; + break; + } + } +} + + +static void wiphy_info_max_roc(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + if (tb) + capa->max_remain_on_chan = nla_get_u32(tb); +} + + +static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, + struct nlattr *ext_setup) +{ + if (tdls == NULL) + return; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + + if (ext_setup) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; + } +} + + +static void wiphy_info_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + u32 flags; + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + flags = nla_get_u32(tb); + + if (flags & NL80211_FEATURE_SK_TX_STATUS) + info->data_tx_status = 1; + + if (flags & NL80211_FEATURE_INACTIVITY_TIMER) + capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; + + if (flags & NL80211_FEATURE_SAE) + capa->flags |= WPA_DRIVER_FLAGS_SAE; + + if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) + capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; + + if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) + capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX; + + if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH; + } + + if (flags & NL80211_FEATURE_P2P_GO_CTWIN) + info->p2p_go_ctwindow_supported = 1; + + if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN) + info->have_low_prio_scan = 1; + + if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR) + info->mac_addr_rand_scan_supported = 1; + + if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR) + info->mac_addr_rand_sched_scan_supported = 1; + + if (flags & NL80211_FEATURE_STATIC_SMPS) + capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC; + + if (flags & NL80211_FEATURE_DYNAMIC_SMPS) + capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC; + + if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) + info->wmm_ac_supported = 1; + + if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) + capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES; + + if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES) + capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES; + + if (flags & NL80211_FEATURE_QUIET) + capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET; + + if (flags & NL80211_FEATURE_TX_POWER_INSERTION) + capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION; + + if (flags & NL80211_FEATURE_HT_IBSS) + capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS; +} + + +static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + u32 protocols; + + if (tb == NULL) + return; + + protocols = nla_get_u32(tb); + wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " + "mode"); + capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; + capa->probe_resp_offloads = probe_resp_offload_support(protocols); +} + + +static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1]; + + if (tb == NULL) + return; + + if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG, + tb, NULL)) + return; + + if (triggers[NL80211_WOWLAN_TRIG_ANY]) + capa->wowlan_triggers.any = 1; + if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT]) + capa->wowlan_triggers.disconnect = 1; + if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT]) + capa->wowlan_triggers.magic_pkt = 1; + if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) + capa->wowlan_triggers.gtk_rekey_failure = 1; + if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) + capa->wowlan_triggers.eap_identity_req = 1; + if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) + capa->wowlan_triggers.four_way_handshake = 1; + if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) + capa->wowlan_triggers.rfkill_release = 1; +} + + +static int wiphy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_info_data *info = arg; + struct wpa_driver_capa *capa = info->capa; + struct wpa_driver_nl80211_data *drv = info->drv; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY_NAME]) + os_strlcpy(drv->phyname, + nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) + capa->max_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) + capa->max_sched_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) + capa->max_match_sets = + nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + + if (tb[NL80211_ATTR_MAC_ACL_MAX]) + capa->max_acl_mac_addrs = + nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); + + wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); + wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); + wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); + wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); + + if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " + "off-channel TX"); + capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + } + + if (tb[NL80211_ATTR_ROAM_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); + capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + } + + wiphy_info_max_roc(capa, + tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + + if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) + capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; + + wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], + tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); + + if (tb[NL80211_ATTR_DEVICE_AP_SME]) + info->device_ap_sme = 1; + + wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_probe_resp_offload(capa, + tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); + + if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && + drv->extended_capa == NULL) { + drv->extended_capa = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa) { + os_memcpy(drv->extended_capa, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + drv->extended_capa_len = + nla_len(tb[NL80211_ATTR_EXT_CAPA]); + } + drv->extended_capa_mask = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); + if (drv->extended_capa_mask) { + os_memcpy(drv->extended_capa_mask, + nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]), + nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); + } else { + os_free(drv->extended_capa); + drv->extended_capa = NULL; + drv->extended_capa_len = 0; + } + } + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + switch (vinfo->subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_TEST: + drv->vendor_cmd_test_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_ROAMING: + drv->roaming_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: + drv->dfs_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: + drv->get_features_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; + break; + } + + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + if (tb[NL80211_ATTR_VENDOR_EVENTS]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + wiphy_info_wowlan_triggers(capa, + tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]); + + if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA]) + capa->max_stations = + nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]); + + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, + struct wiphy_info_data *info) +{ + u32 feat; + struct nl_msg *msg; + int flags = 0; + + os_memset(info, 0, sizeof(*info)); + info->capa = &drv->capa; + info->drv = drv; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + flags = NLM_F_DUMP; + msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY); + if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return -1; + } + + if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) + return -1; + + if (info->auth_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_SME; + else if (!info->connect_supported) { + wpa_printf(MSG_INFO, "nl80211: Driver does not support " + "authentication/association or connect commands"); + info->error = 1; + } + + if (info->p2p_go_supported && info->p2p_client_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + if (info->p2p_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface (driver advertised support)"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } + if (info->num_multichan_concurrent > 1) { + wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " + "concurrent (driver advertised support)"); + drv->capa.num_multichan_concurrent = + info->num_multichan_concurrent; + } + if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) + wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support"); + + /* default to 5000 since early versions of mac80211 don't set it */ + if (!drv->capa.max_remain_on_chan) + drv->capa.max_remain_on_chan = 5000; + + if (info->channel_switch_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; + drv->capa.wmm_ac_supported = info->wmm_ac_supported; + + drv->capa.mac_addr_rand_sched_scan_supported = + info->mac_addr_rand_sched_scan_supported; + drv->capa.mac_addr_rand_scan_supported = + info->mac_addr_rand_scan_supported; + + return 0; +} + + +static int dfs_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + int *dfs_capability_ptr = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) { + u32 val; + val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]); + wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u", + val); + *dfs_capability_ptr = val; + } + } + + return NL_SKIP; +} + + +static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int dfs_capability = 0; + int ret; + + if (!drv->dfs_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) { + nlmsg_free(msg); + return; + } + + ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability); + if (!ret && dfs_capability) + drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; +} + + +struct features_info { + u8 *flags; + size_t flags_len; +}; + + +static int features_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct features_info *info = arg; + struct nlattr *nl_vend, *attr; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + if (nl_vend) { + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; + if (attr) { + info->flags = nla_data(attr); + info->flags_len = nla_len(attr); + } + } + + return NL_SKIP; +} + + +static int check_feature(enum qca_wlan_vendor_features feature, + struct features_info *info) +{ + size_t idx = feature / 8; + + return (idx < info->flags_len) && + (info->flags[idx] & BIT(feature % 8)); +} + + +static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct features_info info; + int ret; + + if (!drv->get_features_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) { + nlmsg_free(msg); + return; + } + + os_memset(&info, 0, sizeof(info)); + ret = send_and_recv_msgs(drv, msg, features_info_handler, &info); + if (ret || !info.flags) + return; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; +} + + +int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) +{ + struct wiphy_info_data info; + if (wpa_driver_nl80211_get_info(drv, &info)) + return -1; + + if (info.error) + return -1; + + drv->has_capability = 1; + 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 | + WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B | + WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + + drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; + drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + + /* + * As all cfg80211 drivers must support cases where the AP interface is + * removed without the knowledge of wpa_supplicant/hostapd, e.g., in + * case that the user space daemon has crashed, they must be able to + * cleanup all stations and key entries in the AP tear down flow. Thus, + * this flag can/should always be set for cfg80211 drivers. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT; + + if (!info.device_ap_sme) { + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + + /* + * No AP SME is currently assumed to also indicate no AP MLME + * in the driver/firmware. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; + } + + drv->device_ap_sme = info.device_ap_sme; + drv->poll_command_supported = info.poll_command_supported; + drv->data_tx_status = info.data_tx_status; + drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported; + if (info.set_qos_map_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; + drv->have_low_prio_scan = info.have_low_prio_scan; + + /* + * If poll command and tx status are supported, mac80211 is new enough + * to have everything we need to not need monitor interfaces. + */ + drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; + + if (drv->device_ap_sme && drv->use_monitor) { + /* + * Non-mac80211 drivers may not support monitor interface. + * Make sure we do not get stuck with incorrect capability here + * by explicitly testing this. + */ + if (!info.monitor_supported) { + wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " + "with device_ap_sme since no monitor mode " + "support detected"); + drv->use_monitor = 0; + } + } + + /* + * If we aren't going to use monitor interfaces, but the + * driver doesn't support data TX status, we won't get TX + * status for EAPOL frames. + */ + if (!drv->use_monitor && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + + qca_nl80211_check_dfs_capa(drv); + qca_nl80211_get_features(drv); + + return 0; +} + + +struct phy_info_arg { + u16 *num_modes; + struct hostapd_hw_modes *modes; + int last_mode, last_chan_idx; +}; + +static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, + struct nlattr *ampdu_factor, + struct nlattr *ampdu_density, + struct nlattr *mcs_set) +{ + if (capa) + mode->ht_capab = nla_get_u16(capa); + + if (ampdu_factor) + mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; + + if (ampdu_density) + mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; + + if (mcs_set && nla_len(mcs_set) >= 16) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->mcs_set, mcs, 16); + } +} + + +static void phy_info_vht_capa(struct hostapd_hw_modes *mode, + struct nlattr *capa, + struct nlattr *mcs_set) +{ + if (capa) + mode->vht_capab = nla_get_u32(capa); + + if (mcs_set && nla_len(mcs_set) >= 8) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->vht_mcs_set, mcs, 8); + } +} + + +static void phy_info_freq(struct hostapd_hw_modes *mode, + struct hostapd_channel_data *chan, + struct nlattr *tb_freq[]) +{ + u8 channel; + chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + chan->flag = 0; + chan->dfs_cac_ms = 0; + if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) + chan->chan = channel; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + chan->flag |= HOSTAPD_CHAN_DISABLED; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) + chan->flag |= HOSTAPD_CHAN_NO_IR; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + chan->flag |= HOSTAPD_CHAN_RADAR; + if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY]) + chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY; + if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT]) + chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { + enum nl80211_dfs_state state = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); + + switch (state) { + case NL80211_DFS_USABLE: + chan->flag |= HOSTAPD_CHAN_DFS_USABLE; + break; + case NL80211_DFS_AVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; + break; + case NL80211_DFS_UNAVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; + break; + } + } + + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) { + chan->dfs_cac_ms = nla_get_u32( + tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]); + } +} + + +static int phy_info_freqs(struct phy_info_arg *phy_info, + struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, + }; + int new_channels = 0; + struct hostapd_channel_data *channel; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + struct nlattr *nl_freq; + int rem_freq, idx; + + if (tb == NULL) + return NL_OK; + + nla_for_each_nested(nl_freq, tb, rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + new_channels++; + } + + channel = os_realloc_array(mode->channels, + mode->num_channels + new_channels, + sizeof(struct hostapd_channel_data)); + if (!channel) + return NL_SKIP; + + mode->channels = channel; + mode->num_channels += new_channels; + + idx = phy_info->last_chan_idx; + + nla_for_each_nested(nl_freq, tb, rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + phy_info_freq(mode, &mode->channels[idx], tb_freq); + idx++; + } + phy_info->last_chan_idx = idx; + + return NL_OK; +} + + +static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = + { .type = NLA_FLAG }, + }; + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + struct nlattr *nl_rate; + int rem_rate, idx; + + if (tb == NULL) + return NL_OK; + + nla_for_each_nested(nl_rate, tb, rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } + + mode->rates = os_calloc(mode->num_rates, sizeof(int)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb, rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx] = nla_get_u32( + tb_rate[NL80211_BITRATE_ATTR_RATE]); + idx++; + } + + return NL_OK; +} + + +static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) +{ + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + struct hostapd_hw_modes *mode; + int ret; + + if (phy_info->last_mode != nl_band->nla_type) { + mode = os_realloc_array(phy_info->modes, + *phy_info->num_modes + 1, + sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + os_memset(mode, 0, sizeof(*mode)); + mode->mode = NUM_HOSTAPD_MODES; + mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | + HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; + + /* + * Unsupported VHT MCS stream is defined as value 3, so the VHT + * MCS RX/TX map must be initialized with 0xffff to mark all 8 + * possible streams as unsupported. This will be overridden if + * driver advertises VHT support. + */ + mode->vht_mcs_set[0] = 0xff; + mode->vht_mcs_set[1] = 0xff; + mode->vht_mcs_set[4] = 0xff; + mode->vht_mcs_set[5] = 0xff; + + *(phy_info->num_modes) += 1; + phy_info->last_mode = nl_band->nla_type; + phy_info->last_chan_idx = 0; + } else + mode = &phy_info->modes[*(phy_info->num_modes) - 1]; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], + tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); + phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], + tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); + ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); + if (ret != NL_OK) + return ret; + ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); + if (ret != NL_OK) + return ret; + + return NL_OK; +} + + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + struct nlattr *nl_band; + int rem_band; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) + { + int res = phy_info_band(phy_info, nl_band); + if (res != NL_OK) + return res; + } + + return NL_SKIP; +} + + +static struct hostapd_hw_modes * +wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* heuristic to set up modes */ + for (m = 0; m < *num_modes; m++) { + if (!modes[m].num_channels) + continue; + if (modes[m].channels[0].freq < 4000) { + modes[m].mode = HOSTAPD_MODE_IEEE80211B; + for (i = 0; i < modes[m].num_rates; i++) { + if (modes[m].rates[i] > 200) { + modes[m].mode = HOSTAPD_MODE_IEEE80211G; + break; + } + } + } else if (modes[m].channels[0].freq > 50000) + modes[m].mode = HOSTAPD_MODE_IEEE80211AD; + else + modes[m].mode = HOSTAPD_MODE_IEEE80211A; + } + + /* If only 802.11g mode is included, use it to construct matching + * 802.11b mode data. */ + + for (m = 0; m < *num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) + return modes; /* 802.11b already included */ + if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) + mode11g_idx = m; + } + + if (mode11g_idx < 0) + return modes; /* 2.4 GHz band not supported at all */ + + nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); + if (nmodes == NULL) + return modes; /* Could not add 802.11b mode */ + + mode = &nmodes[*num_modes]; + os_memset(mode, 0, sizeof(*mode)); + (*num_modes)++; + modes = nmodes; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + + mode11g = &modes[mode11g_idx]; + mode->num_channels = mode11g->num_channels; + mode->channels = os_malloc(mode11g->num_channels * + sizeof(struct hostapd_channel_data)); + if (mode->channels == NULL) { + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + os_memcpy(mode->channels, mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); + + mode->num_rates = 0; + mode->rates = os_malloc(4 * sizeof(int)); + if (mode->rates == NULL) { + os_free(mode->channels); + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + + for (i = 0; i < mode11g->num_rates; i++) { + if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && + mode11g->rates[i] != 55 && mode11g->rates[i] != 110) + continue; + mode->rates[mode->num_rates] = mode11g->rates[i]; + mode->num_rates++; + if (mode->num_rates == 4) + break; + } + + if (mode->num_rates == 0) { + os_free(mode->channels); + os_free(mode->rates); + (*num_modes)--; + return modes; /* No 802.11b rates */ + } + + wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " + "information"); + + return modes; +} + + +static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40; + } +} + + +static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (!(chan->flag & HOSTAPD_CHAN_HT40)) + continue; + if (chan->freq - 30 >= start && chan->freq - 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40MINUS; + if (chan->freq + 10 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_HT40PLUS; + } +} + + +static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + int c; + struct hostapd_hw_modes *mode = &results->modes[m]; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if ((u32) chan->freq - 10 >= start && + (u32) chan->freq + 10 <= end) + chan->max_tx_power = max_eirp; + } + } +} + + +static void nl80211_reg_rule_ht40(u32 start, u32 end, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode(&results->modes[m], start, end); + } +} + + +static void nl80211_reg_rule_sec(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 20) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode_sec(&results->modes[m], start, end); + } +} + + +static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 70 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_10_70; + + if (chan->freq - 30 >= start && chan->freq + 50 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_30_50; + + if (chan->freq - 50 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_50_30; + + if (chan->freq - 70 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_70_10; + } +} + + +static void nl80211_reg_rule_vht(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 80) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + /* TODO: use a real VHT support indication */ + if (!results->modes[m].vht_capab) + continue; + + nl80211_set_vht_mode(&results->modes[m], start, end); + } +} + + +static const char * dfs_domain_name(enum nl80211_dfs_regions region) +{ + switch (region) { + case NL80211_DFS_UNSET: + return "DFS-UNSET"; + case NL80211_DFS_FCC: + return "DFS-FCC"; + case NL80211_DFS_ETSI: + return "DFS-ETSI"; + case NL80211_DFS_JP: + return "DFS-JP"; + default: + return "DFS-invalid"; + } +} + + +static int nl80211_get_reg(struct nl_msg *msg, void *arg) +{ + struct phy_info_arg *results = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *nl_rule; + struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; + int rem_rule; + static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, + }; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || + !tb_msg[NL80211_ATTR_REG_RULES]) { + wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " + "available"); + return NL_SKIP; + } + + if (tb_msg[NL80211_ATTR_DFS_REGION]) { + enum nl80211_dfs_regions dfs_domain; + dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), + dfs_domain_name(dfs_domain)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + u32 start, end, max_eirp = 0, max_bw = 0, flags = 0; + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) + continue; + start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; + if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) + max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS]) + flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]); + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s", + start, end, max_bw, max_eirp, + flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "", + flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "", + flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "", + flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" : + "", + flags & NL80211_RRF_DFS ? " (DFS)" : "", + flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "", + flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "", + flags & NL80211_RRF_NO_IR ? " (no IR)" : ""); + if (max_bw >= 40) + nl80211_reg_rule_ht40(start, end, results); + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + nl80211_reg_rule_max_eirp(start, end, max_eirp, + results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_sec(tb_rule, results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_vht(tb_rule, results); + } + + return NL_SKIP; +} + + +static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, + struct phy_info_arg *results) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); +} + + +struct hostapd_hw_modes * +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + u32 feat; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int nl_flags = 0; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + .last_mode = -1, + }; + + *num_modes = 0; + *flags = 0; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl_flags = NLM_F_DUMP; + if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) || + nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return NULL; + } + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + nl80211_set_regulatory_flags(drv, &result); + return wpa_driver_nl80211_postprocess_modes(result.modes, + num_modes); + } + + return NULL; +} diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c new file mode 100644 index 000000000000..87e412dc596a --- /dev/null +++ b/src/drivers/driver_nl80211_event.c @@ -0,0 +1,2029 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Event processing + * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2007, Johannes Berg + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/qca-vendor.h" +#include "common/qca-vendor-attr.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "driver_nl80211.h" + + +static const char * nl80211_command_to_string(enum nl80211_commands cmd) +{ +#define C2S(x) case x: return #x; + switch (cmd) { + C2S(NL80211_CMD_UNSPEC) + C2S(NL80211_CMD_GET_WIPHY) + C2S(NL80211_CMD_SET_WIPHY) + C2S(NL80211_CMD_NEW_WIPHY) + C2S(NL80211_CMD_DEL_WIPHY) + C2S(NL80211_CMD_GET_INTERFACE) + C2S(NL80211_CMD_SET_INTERFACE) + C2S(NL80211_CMD_NEW_INTERFACE) + C2S(NL80211_CMD_DEL_INTERFACE) + C2S(NL80211_CMD_GET_KEY) + C2S(NL80211_CMD_SET_KEY) + C2S(NL80211_CMD_NEW_KEY) + C2S(NL80211_CMD_DEL_KEY) + C2S(NL80211_CMD_GET_BEACON) + C2S(NL80211_CMD_SET_BEACON) + C2S(NL80211_CMD_START_AP) + C2S(NL80211_CMD_STOP_AP) + C2S(NL80211_CMD_GET_STATION) + C2S(NL80211_CMD_SET_STATION) + C2S(NL80211_CMD_NEW_STATION) + C2S(NL80211_CMD_DEL_STATION) + C2S(NL80211_CMD_GET_MPATH) + C2S(NL80211_CMD_SET_MPATH) + C2S(NL80211_CMD_NEW_MPATH) + C2S(NL80211_CMD_DEL_MPATH) + C2S(NL80211_CMD_SET_BSS) + C2S(NL80211_CMD_SET_REG) + C2S(NL80211_CMD_REQ_SET_REG) + C2S(NL80211_CMD_GET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) + C2S(NL80211_CMD_GET_REG) + C2S(NL80211_CMD_GET_SCAN) + C2S(NL80211_CMD_TRIGGER_SCAN) + C2S(NL80211_CMD_NEW_SCAN_RESULTS) + C2S(NL80211_CMD_SCAN_ABORTED) + C2S(NL80211_CMD_REG_CHANGE) + C2S(NL80211_CMD_AUTHENTICATE) + C2S(NL80211_CMD_ASSOCIATE) + C2S(NL80211_CMD_DEAUTHENTICATE) + C2S(NL80211_CMD_DISASSOCIATE) + C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) + C2S(NL80211_CMD_REG_BEACON_HINT) + C2S(NL80211_CMD_JOIN_IBSS) + C2S(NL80211_CMD_LEAVE_IBSS) + C2S(NL80211_CMD_TESTMODE) + C2S(NL80211_CMD_CONNECT) + C2S(NL80211_CMD_ROAM) + C2S(NL80211_CMD_DISCONNECT) + C2S(NL80211_CMD_SET_WIPHY_NETNS) + C2S(NL80211_CMD_GET_SURVEY) + C2S(NL80211_CMD_NEW_SURVEY_RESULTS) + C2S(NL80211_CMD_SET_PMKSA) + C2S(NL80211_CMD_DEL_PMKSA) + C2S(NL80211_CMD_FLUSH_PMKSA) + C2S(NL80211_CMD_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_SET_TX_BITRATE_MASK) + C2S(NL80211_CMD_REGISTER_FRAME) + C2S(NL80211_CMD_FRAME) + C2S(NL80211_CMD_FRAME_TX_STATUS) + C2S(NL80211_CMD_SET_POWER_SAVE) + C2S(NL80211_CMD_GET_POWER_SAVE) + C2S(NL80211_CMD_SET_CQM) + C2S(NL80211_CMD_NOTIFY_CQM) + C2S(NL80211_CMD_SET_CHANNEL) + C2S(NL80211_CMD_SET_WDS_PEER) + C2S(NL80211_CMD_FRAME_WAIT_CANCEL) + C2S(NL80211_CMD_JOIN_MESH) + C2S(NL80211_CMD_LEAVE_MESH) + C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) + C2S(NL80211_CMD_UNPROT_DISASSOCIATE) + C2S(NL80211_CMD_NEW_PEER_CANDIDATE) + C2S(NL80211_CMD_GET_WOWLAN) + C2S(NL80211_CMD_SET_WOWLAN) + C2S(NL80211_CMD_START_SCHED_SCAN) + C2S(NL80211_CMD_STOP_SCHED_SCAN) + C2S(NL80211_CMD_SCHED_SCAN_RESULTS) + C2S(NL80211_CMD_SCHED_SCAN_STOPPED) + C2S(NL80211_CMD_SET_REKEY_OFFLOAD) + C2S(NL80211_CMD_PMKSA_CANDIDATE) + C2S(NL80211_CMD_TDLS_OPER) + C2S(NL80211_CMD_TDLS_MGMT) + C2S(NL80211_CMD_UNEXPECTED_FRAME) + C2S(NL80211_CMD_PROBE_CLIENT) + C2S(NL80211_CMD_REGISTER_BEACONS) + C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) + C2S(NL80211_CMD_SET_NOACK_MAP) + C2S(NL80211_CMD_CH_SWITCH_NOTIFY) + C2S(NL80211_CMD_START_P2P_DEVICE) + C2S(NL80211_CMD_STOP_P2P_DEVICE) + C2S(NL80211_CMD_CONN_FAILED) + C2S(NL80211_CMD_SET_MCAST_RATE) + C2S(NL80211_CMD_SET_MAC_ACL) + C2S(NL80211_CMD_RADAR_DETECT) + C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) + C2S(NL80211_CMD_UPDATE_FT_IES) + C2S(NL80211_CMD_FT_EVENT) + C2S(NL80211_CMD_CRIT_PROTOCOL_START) + C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) + C2S(NL80211_CMD_GET_COALESCE) + C2S(NL80211_CMD_SET_COALESCE) + C2S(NL80211_CMD_CHANNEL_SWITCH) + C2S(NL80211_CMD_VENDOR) + C2S(NL80211_CMD_SET_QOS_MAP) + C2S(NL80211_CMD_ADD_TX_TS) + C2S(NL80211_CMD_DEL_TX_TS) + default: + return "NL80211_CMD_UNKNOWN"; + } +#undef C2S +} + + +static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->force_connect_cmd) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore auth event when using driver SME"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24 + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_DEBUG, "nl80211: Too short association event " + "frame"); + return; + } + + os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); + if (len > 24 + sizeof(mgmt->u.auth)) { + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); + } + + wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); +} + + +static void nl80211_parse_wmm_params(struct nlattr *wmm_attr, + struct wmm_params *wmm_params) +{ + struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1]; + static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = { + [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, + }; + + if (!wmm_attr || + nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr, + wme_policy) || + !wmm_info[NL80211_STA_WME_UAPSD_QUEUES]) + return; + + wmm_params->uapsd_queues = + nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]); + wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO; +} + + +static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len, struct nlattr *wmm) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 status; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->force_connect_cmd) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore assoc event when using driver SME"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Associate event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24 + sizeof(mgmt->u.assoc_resp)) { + wpa_printf(MSG_DEBUG, "nl80211: Too short association event " + "frame"); + return; + } + + status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (status != WLAN_STATUS_SUCCESS) { + os_memset(&event, 0, sizeof(event)); + event.assoc_reject.bssid = mgmt->bssid; + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_reject.resp_ies = + (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_reject.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } + event.assoc_reject.status_code = status; + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } + + drv->associated = 1; + os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); + os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); + + os_memset(&event, 0, sizeof(event)); + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_info.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } + + event.assoc_info.freq = drv->assoc_freq; + + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); +} + + +static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *status, + struct nlattr *addr, struct nlattr *req_ie, + struct nlattr *resp_ie, + struct nlattr *authorized, + struct nlattr *key_replay_ctr, + struct nlattr *ptk_kck, + struct nlattr *ptk_kek) +{ + union wpa_event_data event; + u16 status_code; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " + "when using userspace SME", cmd); + return; + } + + status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS; + + if (cmd == NL80211_CMD_CONNECT) { + wpa_printf(MSG_DEBUG, + "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)", + status_code, drv->ignore_next_local_disconnect); + } else if (cmd == NL80211_CMD_ROAM) { + wpa_printf(MSG_DEBUG, "nl80211: Roam event"); + } + + os_memset(&event, 0, sizeof(event)); + if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) { + if (addr) + event.assoc_reject.bssid = nla_data(addr); + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (!event.assoc_reject.bssid || + (os_memcmp(event.assoc_reject.bssid, + drv->auth_attempt_bssid, + ETH_ALEN) != 0)) { + /* + * Ignore the event that came without a BSSID or + * for the old connection since this is likely + * not relevant to the new Connect command. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore connection failure event triggered during reassociation"); + return; + } + } + if (resp_ie) { + event.assoc_reject.resp_ies = nla_data(resp_ie); + event.assoc_reject.resp_ies_len = nla_len(resp_ie); + } + event.assoc_reject.status_code = status_code; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } + + drv->associated = 1; + if (addr) { + os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + } + + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } + if (resp_ie) { + event.assoc_info.resp_ies = nla_data(resp_ie); + event.assoc_info.resp_ies_len = nla_len(resp_ie); + } + + event.assoc_info.freq = nl80211_get_assoc_freq(drv); + + if (authorized && nla_get_u8(authorized)) { + event.assoc_info.authorized = 1; + wpa_printf(MSG_DEBUG, "nl80211: connection authorized"); + } + if (key_replay_ctr) { + event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr); + event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr); + } + if (ptk_kck) { + event.assoc_info.ptk_kck = nla_data(ptk_kck); + event.assoc_info.ptk_kck_len = nla_len(ptk_kck); + } + if (ptk_kek) { + event.assoc_info.ptk_kek = nla_data(ptk_kek); + event.assoc_info.ptk_kek_len = nla_len(ptk_kek); + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); +} + + +static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, + struct nlattr *reason, struct nlattr *addr, + struct nlattr *by_ap) +{ + union wpa_event_data data; + unsigned int locally_generated = by_ap == NULL; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two disassociation events that could + * confuse the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event when using userspace SME"); + return; + } + + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event triggered during reassociation"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local " + "disconnect but got another disconnect " + "event first"); + } + + wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); + nl80211_mark_disconnected(drv); + os_memset(&data, 0, sizeof(data)); + if (reason) + data.deauth_info.reason_code = nla_get_u16(reason); + data.deauth_info.locally_generated = by_ap == NULL; + wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); +} + + +static int calculate_chan_offset(int width, int freq, int cf1, int cf2) +{ + int freq1 = 0; + + switch (convert2width(width)) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + return 0; + case CHAN_WIDTH_40: + freq1 = cf1 - 10; + break; + case CHAN_WIDTH_80: + freq1 = cf1 - 30; + break; + case CHAN_WIDTH_160: + freq1 = cf1 - 70; + break; + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_80P80: + /* FIXME: implement this */ + return 0; + } + + return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; +} + + +static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, + struct nlattr *ifindex, struct nlattr *freq, + struct nlattr *type, struct nlattr *bw, + struct nlattr *cf1, struct nlattr *cf2) +{ + struct i802_bss *bss; + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + int ifidx; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + + if (!freq) + return; + + ifidx = nla_get_u32(ifindex); + bss = get_bss_ifindex(drv, ifidx); + if (bss == NULL) { + wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", + ifidx); + return; + } + + if (type) { + enum nl80211_channel_type ch_type = nla_get_u32(type); + + wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type); + switch (ch_type) { + case NL80211_CHAN_NO_HT: + ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + chan_offset = -1; + break; + } + } else if (bw && cf1) { + /* This can happen for example with VHT80 ch switch */ + chan_offset = calculate_chan_offset(nla_get_u32(bw), + nla_get_u32(freq), + nla_get_u32(cf1), + cf2 ? nla_get_u32(cf2) : 0); + } else { + wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); + } + + os_memset(&data, 0, sizeof(data)); + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + if (bw) + data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); + if (cf1) + data.ch_switch.cf1 = nla_get_u32(cf1); + if (cf2) + data.ch_switch.cf2 = nla_get_u32(cf2); + + bss->freq = data.ch_switch.freq; + + wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data); +} + + +static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *addr) +{ + union wpa_event_data event; + enum wpa_event_type ev; + + if (nla_len(addr) != ETH_ALEN) + return; + + wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, + cmd, MAC2STR((u8 *) nla_data(addr))); + + if (cmd == NL80211_CMD_AUTHENTICATE) + ev = EVENT_AUTH_TIMED_OUT; + else if (cmd == NL80211_CMD_ASSOCIATE) + ev = EVENT_ASSOC_TIMED_OUT; + else + return; + + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); + wpa_supplicant_event(drv->ctx, ev, &event); +} + + +static void mlme_event_mgmt(struct i802_bss *bss, + struct nlattr *freq, struct nlattr *sig, + const u8 *frame, size_t len) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 fc, stype; + int ssi_signal = 0; + int rx_freq = 0; + + wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24) { + wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); + return; + } + + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + if (sig) + ssi_signal = (s32) nla_get_u32(sig); + + os_memset(&event, 0, sizeof(event)); + if (freq) { + event.rx_mgmt.freq = nla_get_u32(freq); + rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; + } + wpa_printf(MSG_DEBUG, + "nl80211: RX frame sa=" MACSTR + " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u", + MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc, + le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), + (unsigned int) len); + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.drv_priv = bss; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); +} + + +static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) +{ + union wpa_event_data event; + const struct ieee80211_hdr *hdr; + u16 fc; + + wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); + if (!is_ap_interface(drv->nlmode)) { + u64 cookie_val; + + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" + " cookie=0%llx%s (ack=%d)", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)", ack != NULL); + if (cookie_val != drv->send_action_cookie) + return; + } + + hdr = (const struct ieee80211_hdr *) frame; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = frame; + event.tx_status.data_len = len; + event.tx_status.ack = ack != NULL; + wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); +} + + +static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + const u8 *bssid = NULL; + u16 reason_code = 0; + + if (type == EVENT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; + + if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + !drv->associated && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { + /* + * Avoid issues with some roaming cases where + * disconnection event for the old AP may show up after + * we have started connection with the new AP. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); + return; + } + + if (drv->associated != 0 && + os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { + /* + * We have presumably received this deauth as a + * response to a clear_state_mismatch() outgoing + * deauth. Don't let it take us offline! + */ + wpa_printf(MSG_DEBUG, "nl80211: Deauth received " + "from Unknown BSSID " MACSTR " -- ignoring", + MAC2STR(bssid)); + return; + } + } + + nl80211_mark_disconnected(drv); + os_memset(&event, 0, sizeof(event)); + + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_DISASSOC) { + event.disassoc_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } + } else { + if (drv->ignore_deauth_event) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth"); + drv->ignore_deauth_event = 0; + return; + } + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); + if (drv->ignore_next_local_deauth) { + drv->ignore_next_local_deauth = 0; + if (event.deauth_info.locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first"); + } + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 reason_code = 0; + + if (type == EVENT_UNPROT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); + + if (len < 24) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + + os_memset(&event, 0, sizeof(event)); + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_UNPROT_DISASSOC) { + event.unprot_disassoc.sa = mgmt->sa; + event.unprot_disassoc.da = mgmt->da; + event.unprot_disassoc.reason_code = reason_code; + } else { + event.unprot_deauth.sa = mgmt->sa; + event.unprot_deauth.da = mgmt->da; + event.unprot_deauth.reason_code = reason_code; + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event(struct i802_bss *bss, + enum nl80211_commands cmd, struct nlattr *frame, + struct nlattr *addr, struct nlattr *timed_out, + struct nlattr *freq, struct nlattr *ack, + struct nlattr *cookie, struct nlattr *sig, + struct nlattr *wmm) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + const u8 *data; + size_t len; + + if (timed_out && addr) { + mlme_timeout_event(drv, cmd, addr); + return; + } + + if (frame == NULL) { + wpa_printf(MSG_DEBUG, + "nl80211: MLME event %d (%s) without frame data", + cmd, nl80211_command_to_string(cmd)); + return; + } + + data = nla_data(frame); + len = nla_len(frame); + if (len < 4 + 2 * ETH_ALEN) { + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" + MACSTR ") - too short", + cmd, nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr)); + return; + } + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR + ") A1=" MACSTR " A2=" MACSTR, cmd, + nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr), MAC2STR(data + 4), + MAC2STR(data + 4 + ETH_ALEN)); + if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && + os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && + os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " + "for foreign address", bss->ifname); + return; + } + wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", + nla_data(frame), nla_len(frame)); + + switch (cmd) { + case NL80211_CMD_AUTHENTICATE: + mlme_event_auth(drv, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_ASSOCIATE: + mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm); + break; + case NL80211_CMD_DEAUTHENTICATE: + mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_DISASSOCIATE: + mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_FRAME: + mlme_event_mgmt(bss, freq, sig, nla_data(frame), + nla_len(frame)); + break; + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); + break; + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + default: + break; + } +} + + +static void mlme_event_michael_mic_failure(struct i802_bss *bss, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_MAC]) { + wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", + nla_data(tb[NL80211_ATTR_MAC]), + nla_len(tb[NL80211_ATTR_MAC])); + data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); + } + if (tb[NL80211_ATTR_KEY_SEQ]) { + wpa_hexdump(MSG_DEBUG, "nl80211: TSC", + nla_data(tb[NL80211_ATTR_KEY_SEQ]), + nla_len(tb[NL80211_ATTR_KEY_SEQ])); + } + if (tb[NL80211_ATTR_KEY_TYPE]) { + enum nl80211_key_type key_type = + nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); + wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); + if (key_type == NL80211_KEYTYPE_PAIRWISE) + data.michael_mic_failure.unicast = 1; + } else + data.michael_mic_failure.unicast = 1; + + if (tb[NL80211_ATTR_KEY_IDX]) { + u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); + wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); + } + + wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +} + + +static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + unsigned int freq; + + if (tb[NL80211_ATTR_MAC] == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " + "event"); + return; + } + os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + drv->associated = 1; + wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", + MAC2STR(drv->bssid)); + + freq = nl80211_get_assoc_freq(drv); + if (freq) { + wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz", + freq); + drv->first_bss->freq = freq; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); +} + + +static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, + int cancel_event, struct nlattr *tb[]) +{ + unsigned int freq, chan_type, duration; + union wpa_event_data data; + u64 cookie; + + if (tb[NL80211_ATTR_WIPHY_FREQ]) + freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + else + freq = 0; + + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) + chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + else + chan_type = 0; + + if (tb[NL80211_ATTR_DURATION]) + duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); + else + duration = 0; + + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + else + cookie = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " + "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", + cancel_event, freq, chan_type, duration, + (long long unsigned int) cookie, + cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); + + if (cookie != drv->remain_on_chan_cookie) + return; /* not for us */ + + if (cancel_event) + drv->pending_remain_on_chan = 0; + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = freq; + data.remain_on_channel.duration = duration; + wpa_supplicant_event(drv->ctx, cancel_event ? + EVENT_CANCEL_REMAIN_ON_CHANNEL : + EVENT_REMAIN_ON_CHANNEL, &data); +} + + +static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + os_memset(&data, 0, sizeof(data)); + + if (tb[NL80211_ATTR_IE]) { + data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); + data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + + if (tb[NL80211_ATTR_IE_RIC]) { + data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); + data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(data.ft_ies.target_ap, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, + MAC2STR(data.ft_ies.target_ap)); + + wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data); +} + + +static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, + struct nlattr *tb[]) +{ + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; +#define MAX_REPORT_FREQS 50 + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; + + if (drv->scan_for_auth) { + drv->scan_for_auth = 0; + wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " + "cfg80211 BSS entry"); + wpa_driver_nl80211_authenticate_retry(drv); + return; + } + + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; + + if (tb[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + char msg[200], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) + { + freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (!os_snprintf_error(end - pos, res)) + pos += res; + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + info->freqs = freqs; + info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + enum nl80211_cqm_rssi_threshold_event event; + union wpa_event_data ed; + struct wpa_signal_info sig; + int res; + + if (tb[NL80211_ATTR_CQM] == NULL || + nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], + cqm_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); + return; + } + + os_memset(&ed, 0, sizeof(ed)); + + if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { + if (!tb[NL80211_ATTR_MAC]) + return; + os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + return; + event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + + if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI high"); + ed.signal_change.above_threshold = 1; + } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI low"); + ed.signal_change.above_threshold = 0; + } else + return; + + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig.current_signal; + ed.signal_change.current_txrate = sig.current_txrate; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", + sig.current_signal, sig.current_txrate); + } + + res = nl80211_get_link_noise(drv, &sig); + if (res == 0) { + ed.signal_change.current_noise = sig.current_noise; + wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", + sig.current_noise); + } + + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); +} + + +static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + const u8 *addr; + union wpa_event_data data; + + if (drv->nlmode != NL80211_IFTYPE_MESH_POINT || + !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE]) + return; + + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR, + MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + data.mesh_peer.peer = addr; + data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]); + data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]); + wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data); +} + + +static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + u8 *ies = NULL; + size_t ies_len = 0; + if (tb[NL80211_ATTR_IE]) { + ies = nla_data(tb[NL80211_ATTR_IE]); + ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); + drv_event_assoc(bss->ctx, addr, ies, ies_len, 0); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); + wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data); +} + + +static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, + MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + drv_event_disassoc(drv->ctx, addr); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); +} + + +static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; + static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { + .minlen = NL80211_KEK_LEN, + .maxlen = NL80211_KEK_LEN, + }, + [NL80211_REKEY_DATA_KCK] = { + .minlen = NL80211_KCK_LEN, + .maxlen = NL80211_KCK_LEN, + }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { + .minlen = NL80211_REPLAY_CTR_LEN, + .maxlen = NL80211_REPLAY_CTR_LEN, + }, + }; + union wpa_event_data data; + + if (!tb[NL80211_ATTR_MAC] || + !tb[NL80211_ATTR_REKEY_DATA] || + nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, + tb[NL80211_ATTR_REKEY_DATA], rekey_policy) || + !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) + return; + + os_memset(&data, 0, sizeof(data)); + data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, + MAC2STR(data.driver_gtk_rekey.bssid)); + data.driver_gtk_rekey.replay_ctr = + nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); + wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", + data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); +} + + +static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; + static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { + [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, + [NL80211_PMKSA_CANDIDATE_BSSID] = { + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, + }; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); + + if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] || + nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, + tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) || + !cand[NL80211_PMKSA_CANDIDATE_INDEX] || + !cand[NL80211_PMKSA_CANDIDATE_BSSID]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, + nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); + data.pmkid_candidate.index = + nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); + data.pmkid_candidate.preauth = + cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.client_poll.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); +} + + +static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { + case NL80211_TDLS_SETUP: + wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_SETUP; + break; + case NL80211_TDLS_TEARDOWN: + wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_TEARDOWN; + break; + case NL80211_TDLS_DISCOVERY_REQ: + wpa_printf(MSG_DEBUG, + "nl80211: TDLS discovery request for peer " MACSTR, + MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_DISCOVER; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " + "event"); + return; + } + if (tb[NL80211_ATTR_REASON_CODE]) { + data.tdls.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + } + + wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); +} + + +static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); +} + + +static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + u32 reason; + + wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.connect_failed_reason.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); + switch (reason) { + case NL80211_CONN_FAIL_MAX_CLIENTS: + wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); + data.connect_failed_reason.code = MAX_CLIENT_REACHED; + break; + case NL80211_CONN_FAIL_BLOCKED_CLIENT: + wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR + " tried to connect", + MAC2STR(data.connect_failed_reason.addr)); + data.connect_failed_reason.code = BLOCKED_CLIENT; + break; + default: + wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " + "%u", reason); + return; + } + + wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); +} + + +static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + enum nl80211_radar_event event_type; + + if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) + return; + + os_memset(&data, 0, sizeof(data)); + data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); + + /* Check HT params */ + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + data.dfs_event.ht_enabled = 1; + data.dfs_event.chan_offset = 0; + + switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + data.dfs_event.ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + data.dfs_event.chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + data.dfs_event.chan_offset = -1; + break; + } + } + + /* Get VHT params */ + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + data.dfs_event.chan_width = + convert2width(nla_get_u32( + tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", + data.dfs_event.freq, data.dfs_event.ht_enabled, + data.dfs_event.chan_offset, data.dfs_event.chan_width, + data.dfs_event.cf1, data.dfs_event.cf2); + + switch (event_type) { + case NL80211_RADAR_DETECTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); + break; + case NL80211_RADAR_CAC_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); + break; + case NL80211_RADAR_CAC_ABORTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); + break; + case NL80211_RADAR_NOP_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " + "received", event_type); + break; + } +} + + +static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, + int wds) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data event; + + if (!tb[NL80211_ATTR_MAC]) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = bss->addr; + event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); + event.rx_from_unknown.wds = wds; + + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + u32 i, count; + union wpa_event_data event; + struct wpa_freq_range *range = NULL; + const struct qca_avoid_freq_list *freq_range; + + freq_range = (const struct qca_avoid_freq_list *) data; + if (len < sizeof(freq_range->count)) + return; + + count = freq_range->count; + if (len < sizeof(freq_range->count) + + count * sizeof(struct qca_avoid_freq_range)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", + (unsigned int) len); + return; + } + + if (count > 0) { + range = os_calloc(count, sizeof(struct wpa_freq_range)); + if (range == NULL) + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < count; i++) { + unsigned int idx = event.freq_range.num; + range[idx].min = freq_range->range[i].start_freq; + range[idx].max = freq_range->range[i].end_freq; + wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", + range[idx].min, range[idx].max); + if (range[idx].min > range[idx].max) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); + continue; + } + event.freq_range.num++; + } + event.freq_range.range = range; + + wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); + + os_free(range); +} + + +static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, + "nl80211: ACS channel selection vendor event received"); + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] || + !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]) + return; + + os_memset(&event, 0, sizeof(event)); + event.acs_selected_channels.pri_channel = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]); + event.acs_selected_channels.sec_channel = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]); + + wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); +} + + +static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1]; + u8 *bssid; + + wpa_printf(MSG_DEBUG, + "nl80211: Key management roam+auth vendor event received"); + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] || + nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED]) + return; + + bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]); + wpa_printf(MSG_DEBUG, " * roam BSSID " MACSTR, MAC2STR(bssid)); + + mlme_event_connect(drv, NL80211_CMD_ROAM, NULL, + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]); +} + + +static void qca_nl80211_dfs_offload_radar_event( + struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length) +{ + union wpa_event_data data; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + wpa_printf(MSG_DEBUG, + "nl80211: DFS offload radar vendor event received"); + + if (nla_parse(tb, NL80211_ATTR_MAX, + (struct nlattr *) msg, length, NULL)) + return; + + if (!tb[NL80211_ATTR_WIPHY_FREQ]) { + wpa_printf(MSG_INFO, + "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event"); + return; + } + + os_memset(&data, 0, sizeof(data)); + data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz", + data.dfs_event.freq); + + /* Check HT params */ + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + data.dfs_event.ht_enabled = 1; + data.dfs_event.chan_offset = 0; + + switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + data.dfs_event.ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + data.dfs_event.chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + data.dfs_event.chan_offset = -1; + break; + } + } + + /* Get VHT params */ + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + data.dfs_event.chan_width = + convert2width(nla_get_u32( + tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, " + "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", + data.dfs_event.freq, data.dfs_event.ht_enabled, + data.dfs_event.chan_offset, data.dfs_event.chan_width, + data.dfs_event.cf1, data.dfs_event.cf2); + + switch (subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); + break; + default: + wpa_printf(MSG_DEBUG, + "nl80211: Unknown DFS offload radar event %d received", + subcmd); + break; + } +} + + +static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, + u32 subcmd, u8 *data, size_t len) +{ + switch (subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_TEST: + wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: + qca_nl80211_avoid_freq(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: + qca_nl80211_key_mgmt_auth(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + qca_nl80211_acs_select_ch(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: + case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: + qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len); + break; + default: + wpa_printf(MSG_DEBUG, + "nl80211: Ignore unsupported QCA vendor event %u", + subcmd); + break; + } +} + + +static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u32 vendor_id, subcmd, wiphy = 0; + int wiphy_idx; + u8 *data = NULL; + size_t len = 0; + + if (!tb[NL80211_ATTR_VENDOR_ID] || + !tb[NL80211_ATTR_VENDOR_SUBCMD]) + return; + + vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); + + if (tb[NL80211_ATTR_WIPHY]) + wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", + wiphy, vendor_id, subcmd); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); + len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); + wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); + } + + wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); + if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", + wiphy, wiphy_idx); + return; + } + + switch (vendor_id) { + case OUI_QCA: + nl80211_vendor_event_qca(drv, subcmd, data, len); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); + break; + } +} + + +static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + enum nl80211_reg_initiator init; + + wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + + if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]); + wpa_printf(MSG_DEBUG, " * initiator=%d", init); + switch (init) { + case NL80211_REGDOM_SET_BY_CORE: + data.channel_list_changed.initiator = REGDOM_SET_BY_CORE; + break; + case NL80211_REGDOM_SET_BY_USER: + data.channel_list_changed.initiator = REGDOM_SET_BY_USER; + break; + case NL80211_REGDOM_SET_BY_DRIVER: + data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER; + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE; + break; + } + + if (tb[NL80211_ATTR_REG_TYPE]) { + enum nl80211_reg_type type; + type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); + wpa_printf(MSG_DEBUG, " * type=%d", type); + switch (type) { + case NL80211_REGDOM_TYPE_COUNTRY: + data.channel_list_changed.type = REGDOM_TYPE_COUNTRY; + break; + case NL80211_REGDOM_TYPE_WORLD: + data.channel_list_changed.type = REGDOM_TYPE_WORLD; + break; + case NL80211_REGDOM_TYPE_CUSTOM_WORLD: + data.channel_list_changed.type = + REGDOM_TYPE_CUSTOM_WORLD; + break; + case NL80211_REGDOM_TYPE_INTERSECTION: + data.channel_list_changed.type = + REGDOM_TYPE_INTERSECTION; + break; + } + } + + if (tb[NL80211_ATTR_REG_ALPHA2]) { + os_strlcpy(data.channel_list_changed.alpha2, + nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]), + sizeof(data.channel_list_changed.alpha2)); + wpa_printf(MSG_DEBUG, " * alpha2=%s", + data.channel_list_changed.alpha2); + } + + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); +} + + +static void do_process_drv_event(struct i802_bss *bss, int cmd, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", + cmd, nl80211_command_to_string(cmd), bss->ifname); + + if (cmd == NL80211_CMD_ROAM && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { + /* + * Device will use roam+auth vendor event to indicate + * roaming, so ignore the regular roam event. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth", + cmd); + return; + } + + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && + (cmd == NL80211_CMD_NEW_SCAN_RESULTS || + cmd == NL80211_CMD_SCAN_ABORTED)) { + wpa_driver_nl80211_set_mode(drv->first_bss, + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + } + + switch (cmd) { + case NL80211_CMD_TRIGGER_SCAN: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); + drv->scan_state = SCAN_STARTED; + if (drv->scan_for_auth) { + /* + * Cannot indicate EVENT_SCAN_STARTED here since we skip + * EVENT_SCAN_RESULTS in scan_for_auth case and the + * upper layer implementation could get confused about + * scanning state. + */ + wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth"); + break; + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + break; + case NL80211_CMD_START_SCHED_SCAN: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); + drv->scan_state = SCHED_SCAN_STARTED; + break; + case NL80211_CMD_SCHED_SCAN_STOPPED: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); + drv->scan_state = SCHED_SCAN_STOPPED; + wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + break; + case NL80211_CMD_NEW_SCAN_RESULTS: + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New scan results available"); + drv->scan_state = SCAN_COMPLETED; + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + send_scan_event(drv, 0, tb); + break; + case NL80211_CMD_SCHED_SCAN_RESULTS: + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New sched scan results available"); + drv->scan_state = SCHED_SCAN_RESULTS; + send_scan_event(drv, 0, tb); + break; + case NL80211_CMD_SCAN_ABORTED: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); + drv->scan_state = SCAN_ABORTED; + /* + * Need to indicate that scan results are available in order + * not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + send_scan_event(drv, 1, tb); + break; + case NL80211_CMD_AUTHENTICATE: + case NL80211_CMD_ASSOCIATE: + case NL80211_CMD_DEAUTHENTICATE: + case NL80211_CMD_DISASSOCIATE: + case NL80211_CMD_FRAME_TX_STATUS: + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME], + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM], + tb[NL80211_ATTR_STA_WME]); + break; + case NL80211_CMD_CONNECT: + case NL80211_CMD_ROAM: + mlme_event_connect(drv, cmd, + tb[NL80211_ATTR_STATUS_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_REQ_IE], + tb[NL80211_ATTR_RESP_IE], + NULL, NULL, NULL, NULL); + break; + case NL80211_CMD_CH_SWITCH_NOTIFY: + mlme_event_ch_switch(drv, + tb[NL80211_ATTR_IFINDEX], + tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + tb[NL80211_ATTR_CHANNEL_WIDTH], + tb[NL80211_ATTR_CENTER_FREQ1], + tb[NL80211_ATTR_CENTER_FREQ2]); + break; + case NL80211_CMD_DISCONNECT: + mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_DISCONNECTED_BY_AP]); + break; + case NL80211_CMD_MICHAEL_MIC_FAILURE: + mlme_event_michael_mic_failure(bss, tb); + break; + case NL80211_CMD_JOIN_IBSS: + mlme_event_join_ibss(drv, tb); + break; + case NL80211_CMD_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 0, tb); + break; + case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 1, tb); + break; + case NL80211_CMD_NOTIFY_CQM: + nl80211_cqm_event(drv, tb); + break; + case NL80211_CMD_REG_CHANGE: + nl80211_reg_change_event(drv, tb); + break; + case NL80211_CMD_REG_BEACON_HINT: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + os_memset(&data, 0, sizeof(data)); + data.channel_list_changed.initiator = REGDOM_BEACON_HINT; + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + &data); + break; + case NL80211_CMD_NEW_STATION: + nl80211_new_station_event(drv, bss, tb); + break; + case NL80211_CMD_DEL_STATION: + nl80211_del_station_event(drv, tb); + break; + case NL80211_CMD_SET_REKEY_OFFLOAD: + nl80211_rekey_offload_event(drv, tb); + break; + case NL80211_CMD_PMKSA_CANDIDATE: + nl80211_pmksa_candidate_event(drv, tb); + break; + case NL80211_CMD_PROBE_CLIENT: + nl80211_client_probe_event(drv, tb); + break; + case NL80211_CMD_TDLS_OPER: + nl80211_tdls_oper_event(drv, tb); + break; + case NL80211_CMD_CONN_FAILED: + nl80211_connect_failed_event(drv, tb); + break; + case NL80211_CMD_FT_EVENT: + mlme_event_ft_event(drv, tb); + break; + case NL80211_CMD_RADAR_DETECT: + nl80211_radar_event(drv, tb); + break; + case NL80211_CMD_STOP_AP: + nl80211_stop_ap(drv, tb); + break; + case NL80211_CMD_VENDOR: + nl80211_vendor_event(drv, tb); + break; + case NL80211_CMD_NEW_PEER_CANDIDATE: + nl80211_new_peer_candidate(drv, tb); + break; + default: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", cmd); + break; + } +} + + +int process_global_event(struct nl_msg *msg, void *arg) +{ + struct nl80211_global *global = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wpa_driver_nl80211_data *drv, *tmp; + int ifidx = -1; + struct i802_bss *bss; + u64 wdev_id = 0; + int wdev_id_set = 0; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + else if (tb[NL80211_ATTR_WDEV]) { + wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wdev_id_set = 1; + } + + dl_list_for_each_safe(drv, tmp, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + for (bss = drv->first_bss; bss; bss = bss->next) { + if ((ifidx == -1 && !wdev_id_set) || + ifidx == bss->ifindex || + (wdev_id_set && bss->wdev_id_set && + wdev_id == bss->wdev_id)) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)", + gnlh->cmd, ifidx, (long long unsigned int) wdev_id); + } + + return NL_SKIP; +} + + +int process_bss_event(struct nl_msg *msg, void *arg) +{ + struct i802_bss *bss = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", + gnlh->cmd, nl80211_command_to_string(gnlh->cmd), + bss->ifname); + + switch (gnlh->cmd) { + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME], + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM], + tb[NL80211_ATTR_STA_WME]); + break; + case NL80211_CMD_UNEXPECTED_FRAME: + nl80211_spurious_frame(bss, tb, 0); + break; + case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: + nl80211_spurious_frame(bss, tb, 1); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", gnlh->cmd); + break; + } + + return NL_SKIP; +} diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c new file mode 100644 index 000000000000..45385da91f6a --- /dev/null +++ b/src/drivers/driver_nl80211_monitor.c @@ -0,0 +1,491 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface + * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "linux_ioctl.h" +#include "radiotap_iter.h" +#include "driver_nl80211.h" + + +static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.ack = ok; + wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); +} + + +static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr = (void *)buf; + u16 fc; + union wpa_event_data event; + + if (len < sizeof(*hdr)) + return; + + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = hdr->addr2; + event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == + (WLAN_FC_FROMDS | WLAN_FC_TODS); + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void handle_frame(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len, int datarate, int ssi_signal) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + switch (WLAN_FC_GET_TYPE(fc)) { + case WLAN_FC_TYPE_MGMT: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.datarate = datarate; + event.rx_mgmt.ssi_signal = ssi_signal; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_TYPE_CTRL: + /* can only get here with PS-Poll frames */ + wpa_printf(MSG_DEBUG, "CTRL"); + from_unknown_sta(drv, buf, len); + break; + case WLAN_FC_TYPE_DATA: + from_unknown_sta(drv, buf, len); + break; + } +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct ieee80211_radiotap_iterator iter; + int ret; + int datarate = 0, ssi_signal = 0; + int injected = 0, failed = 0, rxflags = 0; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", + strerror(errno)); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) { + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); + return; + } + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", + ret); + return; + } + switch (iter.this_arg_index) { + case IEEE80211_RADIOTAP_FLAGS: + if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) + len -= 4; + break; + case IEEE80211_RADIOTAP_RX_FLAGS: + rxflags = 1; + break; + case IEEE80211_RADIOTAP_TX_FLAGS: + injected = 1; + failed = le_to_host16((*(uint16_t *) iter.this_arg)) & + IEEE80211_RADIOTAP_F_TX_FAIL; + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: + break; + case IEEE80211_RADIOTAP_CHANNEL: + /* TODO: convert from freq/flags to channel number */ + break; + case IEEE80211_RADIOTAP_RATE: + datarate = *iter.this_arg * 5; + break; + case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: + ssi_signal = (s8) *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) + handle_frame(drv, buf + iter._max_length, + len - iter._max_length, datarate, ssi_signal); + else + handle_tx_callback(drv->ctx, buf + iter._max_length, + len - iter._max_length, !failed); +} + + +/* + * we post-process the filter code later and rewrite + * this to the offset to the last instruction + */ +#define PASS 0xFF +#define FAIL 0xFE + +static struct sock_filter msock_filter_insns[] = { + /* + * do a little-endian load of the radiotap length field + */ + /* load lower byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + /* put it into X (== index register) */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + /* load upper byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), + /* left-shift it by 8 */ + BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), + /* or with X */ + BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), + /* put result into X */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + + /* + * Allow management frames through, this also gives us those + * management frames that we sent ourselves with status + */ + /* load the lower byte of the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off frame type and version */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), + /* accept frame if it's both 0, fall through otherwise */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), + + /* + * TODO: add a bit to radiotap RX flags that indicates + * that the sending station is not associated, then + * add a filter here that filters on our DA and that flag + * to allow us to deauth frames to that bad station. + * + * For now allow all To DS data frames through. + */ + /* load the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), + /* mask off frame type, version and DS status */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), + /* accept frame if version 0, type 2 and To DS, fall through otherwise + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), + +#if 0 + /* + * drop non-data frames + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), + /* drop non-data frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), +#endif + /* load the upper byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), + /* mask off toDS/fromDS */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), + /* accept WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), + + /* + * add header length to index + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), + /* right shift it by 6 to give 0 or 2 */ + BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), + /* add data frame header length */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), + /* add index, was start of 802.11 header */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + /* move to index, now start of LL header */ + BPF_STMT(BPF_MISC | BPF_TAX, 0), + + /* + * Accept empty data frames, we use those for + * polling activity. + */ + BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), + + /* + * Accept EAPOL frames + */ + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), + + /* keep these last two statements or change the code below */ + /* return 0 == "DROP" */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* return ~0 == "keep all" */ + BPF_STMT(BPF_RET | BPF_K, ~0), +}; + +static struct sock_fprog msock_filter = { + .len = ARRAY_SIZE(msock_filter_insns), + .filter = msock_filter_insns, +}; + + +static int add_monitor_filter(int s) +{ + int idx; + + /* rewrite all PASS/FAIL jump offsets */ + for (idx = 0; idx < msock_filter.len; idx++) { + struct sock_filter *insn = &msock_filter_insns[idx]; + + if (BPF_CLASS(insn->code) == BPF_JMP) { + if (insn->code == (BPF_JMP|BPF_JA)) { + if (insn->k == PASS) + insn->k = msock_filter.len - idx - 2; + else if (insn->k == FAIL) + insn->k = msock_filter.len - idx - 3; + } + + if (insn->jt == PASS) + insn->jt = msock_filter.len - idx - 2; + else if (insn->jt == FAIL) + insn->jt = msock_filter.len - idx - 3; + + if (insn->jf == PASS) + insn->jf = msock_filter.len - idx - 2; + else if (insn->jf == FAIL) + insn->jf = msock_filter.len - idx - 3; + } + } + + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, + &msock_filter, sizeof(msock_filter))) { + wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv) +{ + if (drv->monitor_refcount > 0) + drv->monitor_refcount--; + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", + drv->monitor_refcount); + if (drv->monitor_refcount > 0) + return; + + if (drv->monitor_ifidx >= 0) { + nl80211_remove_iface(drv, drv->monitor_ifidx); + drv->monitor_ifidx = -1; + } + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + drv->monitor_sock = -1; + } +} + + +int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval; + socklen_t optlen; + + if (drv->monitor_ifidx >= 0) { + drv->monitor_refcount++; + wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", + drv->monitor_refcount); + return 0; + } + + if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { + /* + * P2P interface name is of the format p2p-%s-%d. For monitor + * interface name corresponding to P2P GO, replace "p2p-" with + * "mon-" to retain the same interface name length and to + * indicate that it is a monitor interface. + */ + snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); + } else { + /* Non-P2P interface with AP functionality. */ + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); + } + + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, + 0, NULL, NULL, 0); + + if (drv->monitor_ifidx == -EOPNOTSUPP) { + /* + * This is backward compatibility for a few versions of + * the kernel only that didn't advertise the right + * attributes for the only driver that then supported + * AP mode w/o monitor -- ath6kl. + */ + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " + "monitor interface type - try to run without it"); + drv->device_ap_sme = 1; + } + + if (drv->monitor_ifidx < 0) + return -1; + + if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1)) + goto error; + + memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = drv->monitor_ifidx; + drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->monitor_sock < 0) { + wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", + strerror(errno)); + goto error; + } + + if (add_monitor_filter(drv->monitor_sock)) { + wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " + "interface; do filtering in user space"); + /* This works, but will cost in performance. */ + } + + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", + strerror(errno)); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", + strerror(errno)); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); + goto error; + } + + drv->monitor_refcount++; + return 0; + error: + nl80211_remove_monitor_interface(drv); + return -1; +} + + +int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) +{ + __u8 rtap_hdr[] = { + 0x00, 0x00, /* radiotap version */ + 0x0e, 0x00, /* radiotap length */ + 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ + IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ + 0x00, /* padding */ + 0x00, 0x00, /* RX and TX flags to indicate that */ + 0x00, 0x00, /* this is the injected frame directly */ + }; + struct iovec iov[2] = { + { + .iov_base = &rtap_hdr, + .iov_len = sizeof(rtap_hdr), + }, + { + .iov_base = (void *) data, + .iov_len = len, + } + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = iov, + .msg_iovlen = 2, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + int res; + u16 txflags = 0; + + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + + if (drv->monitor_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " + "for %s", __func__); + return -1; + } + + if (noack) + txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; + WPA_PUT_LE16(&rtap_hdr[12], txflags); + + res = sendmsg(drv->monitor_sock, &msg, 0); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); + return -1; + } + return 0; +} diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c new file mode 100644 index 000000000000..3911f485f72e --- /dev/null +++ b/src/drivers/driver_nl80211_scan.c @@ -0,0 +1,775 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Scanning + * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2007, Johannes Berg + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "driver_nl80211.h" + + +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *scan_results = arg; + struct wpa_scan_res *scan_res; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " + "attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + for (i = 0; i < scan_results->num; ++i) { + scan_res = scan_results->res[i]; + if (!scan_res) + continue; + if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + scan_res->freq) + continue; + if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + scan_res->noise = (s8) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; + } + + return NL_SKIP; +} + + +static int nl80211_get_noise_for_scan_results( + struct wpa_driver_nl80211_data *drv, + struct wpa_scan_results *scan_res) +{ + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, + scan_res); +} + + +/** + * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Driver private data + * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { + wpa_driver_nl80211_set_mode(drv->first_bss, + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + } + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static struct nl_msg * +nl80211_scan_common(struct i802_bss *bss, u8 cmd, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + size_t i; + u32 scan_flags = 0; + + msg = nl80211_cmd_msg(bss, 0, cmd); + if (!msg) + return NULL; + + if (params->num_ssids) { + struct nlattr *ssids; + + ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); + if (ssids == NULL) + goto fail; + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid)) + goto fail; + } + nla_nest_end(msg, ssids); + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies)) + goto fail; + } + + if (params->freqs) { + struct nlattr *freqs; + freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i])) + goto fail; + } + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->only_new_results) { + wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); + scan_flags |= NL80211_SCAN_FLAG_FLUSH; + } + + if (params->low_priority && drv->have_low_prio_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY"); + scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; + } + + if (params->mac_addr_rand) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR"); + scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; + + if (params->mac_addr) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR, + MAC2STR(params->mac_addr)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + params->mac_addr)) + goto fail; + } + + if (params->mac_addr_mask) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: " + MACSTR, MAC2STR(params->mac_addr_mask)); + if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN, + params->mac_addr_mask)) + goto fail; + } + } + + if (scan_flags && + nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + goto fail; + + return msg; + +fail: + nlmsg_free(msg); + return NULL; +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @bss: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_nl80211_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, timeout; + struct nl_msg *msg = NULL; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); + drv->scan_for_auth = 0; + + msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); + if (!msg) + return -1; + + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto fail; + + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + if (nla_put(msg, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c")) + goto fail; + nla_nest_end(msg, rates); + + if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) + goto fail; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " + "(%s)", ret, strerror(-ret)); + if (drv->hostapd && is_ap_interface(drv->nlmode)) { + enum nl80211_iftype old_mode = drv->nlmode; + + /* + * mac80211 does not allow scan requests in AP mode, so + * try to do this in station mode. + */ + if (wpa_driver_nl80211_set_mode( + bss, NL80211_IFTYPE_STATION)) + goto fail; + + if (wpa_driver_nl80211_scan(bss, params)) { + wpa_driver_nl80211_set_mode(bss, old_mode); + goto fail; + } + + /* Restore AP mode when processing scan results */ + drv->ap_scan_as_station = old_mode; + ret = 0; + } else + goto fail; + } + + drv->scan_state = SCAN_REQUESTED; + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 10; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver events to notify when scan is + * complete, so use longer timeout to avoid race conditions + * with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + +fail: + nlmsg_free(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure or if not supported + */ +int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + struct nl_msg *msg; + size_t i; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request"); + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_start(bss, params); +#endif /* ANDROID */ + + msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval)) + goto fail; + + if ((drv->num_filter_ssids && + (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || + params->filter_rssi) { + struct nlattr *match_sets; + match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); + if (match_sets == NULL) + goto fail; + + for (i = 0; i < drv->num_filter_ssids; i++) { + struct nlattr *match_set_ssid; + wpa_hexdump_ascii(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID", + drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len); + + match_set_ssid = nla_nest_start(msg, i + 1); + if (match_set_ssid == NULL || + nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + drv->filter_ssids[i].ssid_len, + drv->filter_ssids[i].ssid) || + (params->filter_rssi && + nla_put_u32(msg, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi))) + goto fail; + + nla_nest_end(msg, match_set_ssid); + } + + /* + * Due to backward compatibility code, newer kernels treat this + * matchset (with only an RSSI filter) as the default for all + * other matchsets, unless it's the only one, in which case the + * matchset will actually allow all SSIDs above the RSSI. + */ + if (params->filter_rssi) { + struct nlattr *match_set_rssi; + match_set_rssi = nla_nest_start(msg, 0); + if (match_set_rssi == NULL || + nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi)) + goto fail; + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan RSSI filter %d dBm", + params->filter_rssi); + nla_nest_end(msg, match_set_rssi); + } + + nla_nest_end(msg, match_sets); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + /* TODO: if we get an error here, we should fall back to normal scan */ + + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto fail; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " + "scan interval %d msec", ret, interval); + +fail: + nlmsg_free(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * Returns: 0 on success, -1 on failure or if not supported + */ +int wpa_driver_nl80211_stop_sched_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + struct nl_msg *msg; + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_stop(bss); +#endif /* ANDROID */ + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Sched scan stop failed: ret=%d (%s)", + ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Sched scan stop sent"); + } + + return ret; +} + + +static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +{ + const u8 *end, *pos; + + if (ies == NULL) + return NULL; + + pos = ies; + end = ies + ies_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 int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, + const u8 *ie, size_t ie_len) +{ + const u8 *ssid; + size_t i; + + if (drv->filter_ssids == NULL) + return 0; + + ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid == NULL) + return 1; + + for (i = 0; i < drv->num_filter_ssids; i++) { + if (ssid[1] == drv->filter_ssids[i].ssid_len && + os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == + 0) + return 0; + } + + return 1; +} + + +int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, + [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, + [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, + [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, + }; + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + const u8 *ie, *beacon_ie; + size_t ie_len, beacon_ie_len; + u8 *pos; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS]) + return NL_SKIP; + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy)) + return NL_SKIP; + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->assoc_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + _arg->assoc_freq); + } + if (status == NL80211_BSS_STATUS_IBSS_JOINED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->ibss_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", + _arg->ibss_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(_arg->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(_arg->assoc_bssid)); + } + } + if (!res) + return NL_SKIP; + if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + } else { + ie = NULL; + ie_len = 0; + } + if (bss[NL80211_BSS_BEACON_IES]) { + beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); + beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); + } else { + beacon_ie = NULL; + beacon_ie_len = 0; + } + + if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + ie ? ie_len : beacon_ie_len)) + return NL_SKIP; + + r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); + if (r == NULL) + return NL_SKIP; + if (bss[NL80211_BSS_BSSID]) + os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), + ETH_ALEN); + if (bss[NL80211_BSS_FREQUENCY]) + r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + if (bss[NL80211_BSS_BEACON_INTERVAL]) + r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); + if (bss[NL80211_BSS_CAPABILITY]) + r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); + r->flags |= WPA_SCAN_NOISE_INVALID; + if (bss[NL80211_BSS_SIGNAL_MBM]) { + r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + r->level /= 100; /* mBm to dBm */ + r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; + } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { + r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + r->flags |= WPA_SCAN_QUAL_INVALID; + } else + r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; + if (bss[NL80211_BSS_TSF]) + r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + if (bss[NL80211_BSS_SEEN_MS_AGO]) + r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); + r->ie_len = ie_len; + pos = (u8 *) (r + 1); + if (ie) { + os_memcpy(pos, ie, ie_len); + pos += ie_len; + } + r->beacon_ie_len = beacon_ie_len; + if (beacon_ie) + os_memcpy(pos, beacon_ie, beacon_ie_len); + + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + switch (status) { + case NL80211_BSS_STATUS_ASSOCIATED: + r->flags |= WPA_SCAN_ASSOCIATED; + break; + default: + break; + } + } + + /* + * cfg80211 maintains separate BSS table entries for APs if the same + * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does + * not use frequency as a separate key in the BSS table, so filter out + * duplicated entries. Prefer associated BSS entry in such a case in + * order to get the correct frequency into the BSS table. Similarly, + * prefer newer entries over older. + */ + for (i = 0; i < res->num; i++) { + const u8 *s1, *s2; + if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) + continue; + + s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || + os_memcmp(s1, s2, 2 + s1[1]) != 0) + continue; + + /* Same BSSID,SSID was already included in scan results */ + wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " + "for " MACSTR, MAC2STR(r->bssid)); + + if (((r->flags & WPA_SCAN_ASSOCIATED) && + !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || + r->age < res->res[i]->age) { + os_free(res->res[i]); + res->res[i] = r; + } else + os_free(r); + return NL_SKIP; + } + + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return NL_SKIP; + } + tmp[res->num++] = r; + res->res = tmp; + + return NL_SKIP; +} + + +static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, + const u8 *addr) +{ + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " + "mismatch (" MACSTR ")", MAC2STR(addr)); + wpa_driver_nl80211_mlme(drv, addr, + NL80211_CMD_DEAUTHENTICATE, + WLAN_REASON_PREV_AUTH_NOT_VALID, 1); + } +} + + +static void wpa_driver_nl80211_check_bss_status( + struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res) +{ + size_t i; + + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + + if (r->flags & WPA_SCAN_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "nl80211: Scan results " + "indicate BSS status with " MACSTR + " as associated", + MAC2STR(r->bssid)); + if (is_sta_interface(drv->nlmode) && + !drv->associated) { + wpa_printf(MSG_DEBUG, "nl80211: Local state " + "(not associated) does not match " + "with BSS state"); + clear_state_mismatch(drv, r->bssid); + } else if (is_sta_interface(drv->nlmode) && + os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "nl80211: Local state " + "(associated with " MACSTR ") does " + "not match with BSS state", + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + clear_state_mismatch(drv, drv->bssid); + } + } + } +} + + +static struct wpa_scan_results * +nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct wpa_scan_results *res; + int ret; + struct nl80211_bss_info_arg arg; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, + NL80211_CMD_GET_SCAN))) { + wpa_scan_results_free(res); + return NULL; + } + + arg.drv = drv; + arg.res = res; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " + "BSSes)", (unsigned long) res->num); + nl80211_get_noise_for_scan_results(drv, res); + return res; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); + wpa_scan_results_free(res); + return NULL; +} + + +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_scan_results *res; + + res = nl80211_get_scan_results(drv); + if (res) + wpa_driver_nl80211_check_bss_status(drv, res); + return res; +} + + +void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +{ + struct wpa_scan_results *res; + size_t i; + + res = nl80211_get_scan_results(drv); + if (res == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s", + (int) i, (int) res->num, MAC2STR(r->bssid), + r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + } + + wpa_scan_results_free(res); +} diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c index d75c14b182f9..6ff3eae6c4f9 100644 --- a/src/drivers/driver_none.c +++ b/src/drivers/driver_none.c @@ -74,13 +74,6 @@ static void none_driver_deinit(void *priv) } -static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto, - const u8 *data, size_t data_len) -{ - return -1; -} - - const struct wpa_driver_ops wpa_driver_none_ops = { .name = "none", .desc = "no driver (RADIUS server/WPS ER)", @@ -89,5 +82,4 @@ const struct wpa_driver_ops wpa_driver_none_ops = { .send_ether = none_driver_send_ether, .init = none_driver_init, .deinit = none_driver_deinit, - .send_eapol = none_driver_send_eapol, }; diff --git a/src/drivers/driver_openbsd.c b/src/drivers/driver_openbsd.c new file mode 100644 index 000000000000..e94eda08f621 --- /dev/null +++ b/src/drivers/driver_openbsd.c @@ -0,0 +1,136 @@ +/* + * Driver interaction with OpenBSD net80211 layer + * Copyright (c) 2013, Mark Kettenis + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" + +struct openbsd_driver_data { + char ifname[IFNAMSIZ + 1]; + void *ctx; + + int sock; /* open socket for 802.11 ioctls */ +}; + + +static int +wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid) +{ + struct openbsd_driver_data *drv = priv; + 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; +} + +static int +wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid) +{ + struct openbsd_driver_data *drv = priv; + struct ieee80211_bssid id; + + os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name)); + if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0) + return -1; + + os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN); + return 0; +} + + +static int +wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + return 0; +} + + +static int +wpa_driver_openbsd_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 openbsd_driver_data *drv = priv; + struct ieee80211_keyavail keyavail; + + if (alg != WPA_ALG_PMK || key_len > IEEE80211_PMK_LEN) + return -1; + + memset(&keyavail, 0, sizeof(keyavail)); + os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name)); + if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0) + return -1; + memcpy(keyavail.i_key, key, key_len); + + if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0) + return -1; + + return 0; +} + +static void * +wpa_driver_openbsd_init(void *ctx, const char *ifname) +{ + struct openbsd_driver_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail; + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; + +fail: + os_free(drv); + return NULL; +} + + +static void +wpa_driver_openbsd_deinit(void *priv) +{ + struct openbsd_driver_data *drv = priv; + + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_openbsd_ops = { + .name = "openbsd", + .desc = "OpenBSD 802.11 support", + .get_ssid = wpa_driver_openbsd_get_ssid, + .get_bssid = wpa_driver_openbsd_get_bssid, + .get_capa = wpa_driver_openbsd_get_capa, + .set_key = wpa_driver_openbsd_set_key, + .init = wpa_driver_openbsd_init, + .deinit = wpa_driver_openbsd_deinit, +}; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index ed88e71c3a77..de23fbd2b9fe 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -35,7 +35,7 @@ static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) (struct sockaddr *) &drv->priv_addr, sizeof(drv->priv_addr)); if (res < 0) - perror("sendto"); + wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno)); return res < 0 ? -1 : 0; } @@ -59,7 +59,8 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, msg.msg_namelen = sizeof(drv->priv_addr); if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { - perror("sendmsg(cmd_socket)"); + wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s", + strerror(errno)); return -1; } @@ -74,14 +75,15 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, tv.tv_usec = 0; res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { - perror("select"); + wpa_printf(MSG_ERROR, "select: %s", strerror(errno)); return -1; } if (FD_ISSET(drv->cmd_socket, &rfds)) { res = recv(drv->cmd_socket, reply, *reply_len, 0); if (res < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", + strerror(errno)); return -1; } *reply_len = res; @@ -228,7 +230,7 @@ static int wpa_driver_privsep_associate( wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", - __func__, priv, params->freq, params->pairwise_suite, + __func__, priv, params->freq.freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); @@ -241,7 +243,9 @@ static int wpa_driver_privsep_associate( os_memcpy(data->bssid, params->bssid, ETH_ALEN); os_memcpy(data->ssid, params->ssid, params->ssid_len); data->ssid_len = params->ssid_len; - data->freq = params->freq; + data->hwmode = params->freq.mode; + data->freq = params->freq.freq; + data->channel = params->freq.channel; data->pairwise_suite = params->pairwise_suite; data->group_suite = params->group_suite; data->key_mgmt_suite = params->key_mgmt_suite; @@ -439,7 +443,8 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, buflen, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(priv_socket)"); + wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s", + strerror(errno)); os_free(buf); return; } @@ -629,7 +634,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->priv_socket < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); os_free(drv->own_socket_path); drv->own_socket_path = NULL; return -1; @@ -640,7 +645,9 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("privsep-set-params priv-sock: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "privsep-set-params priv-sock: bind(PF_UNIX): %s", + strerror(errno)); close(drv->priv_socket); drv->priv_socket = -1; unlink(drv->own_socket_path); @@ -654,7 +661,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->cmd_socket < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); os_free(drv->own_cmd_path); drv->own_cmd_path = NULL; return -1; @@ -665,7 +672,9 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "privsep-set-params cmd-sock: bind(PF_UNIX): %s", + strerror(errno)); close(drv->cmd_socket); drv->cmd_socket = -1; unlink(drv->own_cmd_path); diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index 0a9078a4ab06..d3e05955f7a8 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -91,7 +91,8 @@ static u16 wpa_driver_roboswitch_mdio_read( mii->reg_num = reg; if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { - perror("ioctl[SIOCGMIIREG]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s", + strerror(errno)); return 0x00; } return mii->val_out; @@ -108,7 +109,8 @@ static void wpa_driver_roboswitch_mdio_write( mii->val_in = val; if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { - perror("ioctl[SIOCSMIIREG"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s", + strerror(errno)); } } @@ -260,17 +262,17 @@ static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, ROBO_ARLCTRL_CONF, read1, 1); } else { /* if both multiport addresses are the same we can add */ - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, read1, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, read2, 3); - if (os_memcmp(read1, read2, 6) != 0) + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, read1, 3) || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, read2, 3) || + os_memcmp(read1, read2, 6) != 0) return -1; - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_1, read1, 1); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, read2, 1); - if (read1[0] != read2[0]) + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, read1, 1) || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, read2, 1) || + read1[0] != read2[0]) return -1; wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_ADDR_1, addr_be16, 3); @@ -394,7 +396,8 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) os_memset(&drv->ifr, 0, sizeof(drv->ifr)); os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { - perror("ioctl[SIOCGMIIPHY]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s", + strerror(errno)); os_free(drv); return NULL; } diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c deleted file mode 100644 index bd65dd874d2e..000000000000 --- a/src/drivers/driver_test.c +++ /dev/null @@ -1,3315 +0,0 @@ -/* - * Testing driver interface for a simulated network driver - * Copyright (c) 2004-2010, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ -#include "build_config.h" -#ifdef CONFIG_NATIVE_WINDOWS -#include -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "utils/includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS -#include -#include -#include -#define DRIVER_TEST_UNIX -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "utils/common.h" -#include "utils/eloop.h" -#include "utils/list.h" -#include "utils/trace.h" -#include "common/ieee802_11_defs.h" -#include "crypto/sha1.h" -#include "l2_packet/l2_packet.h" -#include "p2p/p2p.h" -#include "wps/wps.h" -#include "driver.h" - - -struct test_client_socket { - struct test_client_socket *next; - u8 addr[ETH_ALEN]; - struct sockaddr_un un; - socklen_t unlen; - struct test_driver_bss *bss; -}; - -struct test_driver_bss { - struct wpa_driver_test_data *drv; - struct dl_list list; - void *bss_ctx; - char ifname[IFNAMSIZ]; - u8 bssid[ETH_ALEN]; - u8 *ie; - size_t ielen; - u8 *wps_beacon_ie; - size_t wps_beacon_ie_len; - u8 *wps_probe_resp_ie; - size_t wps_probe_resp_ie_len; - u8 ssid[32]; - size_t ssid_len; - int privacy; -}; - -struct wpa_driver_test_global { - int bss_add_used; - u8 req_addr[ETH_ALEN]; -}; - -struct wpa_driver_test_data { - struct wpa_driver_test_global *global; - void *ctx; - WPA_TRACE_REF(ctx); - u8 own_addr[ETH_ALEN]; - int test_socket; -#ifdef DRIVER_TEST_UNIX - struct sockaddr_un hostapd_addr; -#endif /* DRIVER_TEST_UNIX */ - int hostapd_addr_set; - struct sockaddr_in hostapd_addr_udp; - int hostapd_addr_udp_set; - char *own_socket_path; - char *test_dir; -#define MAX_SCAN_RESULTS 30 - struct wpa_scan_res *scanres[MAX_SCAN_RESULTS]; - size_t num_scanres; - int use_associnfo; - u8 assoc_wpa_ie[80]; - size_t assoc_wpa_ie_len; - int associated; - u8 *probe_req_ie; - size_t probe_req_ie_len; - u8 probe_req_ssid[32]; - size_t probe_req_ssid_len; - int ibss; - int ap; - - struct test_client_socket *cli; - struct dl_list bss; - int udp_port; - - int alloc_iface_idx; - - int probe_req_report; - unsigned int remain_on_channel_freq; - unsigned int remain_on_channel_duration; - - int current_freq; - - struct p2p_data *p2p; - 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; - unsigned int pending_action_no_cck; - unsigned int pending_listen_freq; - unsigned int pending_listen_duration; - int pending_p2p_scan; - struct sockaddr *probe_from; - socklen_t probe_from_len; -}; - - -static void wpa_driver_test_deinit(void *priv); -static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, - const char *dir, int ap); -static void wpa_driver_test_close_test_socket( - struct wpa_driver_test_data *drv); -static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); -static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv); - - -static void test_driver_free_bss(struct test_driver_bss *bss) -{ - os_free(bss->ie); - os_free(bss->wps_beacon_ie); - os_free(bss->wps_probe_resp_ie); - os_free(bss); -} - - -static void test_driver_free_bsses(struct wpa_driver_test_data *drv) -{ - struct test_driver_bss *bss, *tmp; - - dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss, - list) { - dl_list_del(&bss->list); - test_driver_free_bss(bss); - } -} - - -static struct test_client_socket * -test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from, - socklen_t fromlen) -{ - struct test_client_socket *cli = drv->cli; - - while (cli) { - if (cli->unlen == fromlen && - strncmp(cli->un.sun_path, from->sun_path, - fromlen - sizeof(cli->un.sun_family)) == 0) - return cli; - cli = cli->next; - } - - return NULL; -} - - -static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, - const u8 *own_addr, u32 flags) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_client_socket *cli; - struct msghdr msg; - struct iovec io[3]; - struct l2_ethhdr eth; - - if (drv->test_socket < 0) - return -1; - - cli = drv->cli; - while (cli) { - if (memcmp(cli->addr, addr, ETH_ALEN) == 0) - break; - cli = cli->next; - } - - if (!cli) { - wpa_printf(MSG_DEBUG, "%s: no destination client entry", - __func__); - return -1; - } - - memcpy(eth.h_dest, addr, ETH_ALEN); - memcpy(eth.h_source, own_addr, ETH_ALEN); - eth.h_proto = host_to_be16(ETH_P_EAPOL); - - io[0].iov_base = "EAPOL "; - io[0].iov_len = 6; - io[1].iov_base = ð - io[1].iov_len = sizeof(eth); - io[2].iov_base = (u8 *) data; - io[2].iov_len = data_len; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 3; - msg.msg_name = &cli->un; - msg.msg_namelen = cli->unlen; - return sendmsg(drv->test_socket, &msg, 0); -} - - -static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src, - u16 proto, const u8 *data, size_t data_len) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct msghdr msg; - struct iovec io[3]; - struct l2_ethhdr eth; - char desttxt[30]; - struct sockaddr_un addr; - struct dirent *dent; - DIR *dir; - int ret = 0, broadcast = 0, count = 0; - - if (drv->test_socket < 0 || drv->test_dir == NULL) { - wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " - "test_dir=%p)", - __func__, drv->test_socket, drv->test_dir); - return -1; - } - - broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; - snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst)); - - memcpy(eth.h_dest, dst, ETH_ALEN); - memcpy(eth.h_source, src, ETH_ALEN); - eth.h_proto = host_to_be16(proto); - - io[0].iov_base = "ETHER "; - io[0].iov_len = 6; - io[1].iov_base = ð - io[1].iov_len = sizeof(eth); - io[2].iov_base = (u8 *) data; - io[2].iov_len = data_len; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 3; - - dir = opendir(drv->test_dir); - if (dir == NULL) { - perror("test_driver: opendir"); - return -1; - } - while ((dent = readdir(dir))) { -#ifdef _DIRENT_HAVE_D_TYPE - /* Skip the file if it is not a socket. Also accept - * DT_UNKNOWN (0) in case the C library or underlying file - * system does not support d_type. */ - if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) - continue; -#endif /* _DIRENT_HAVE_D_TYPE */ - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) - continue; - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", - drv->test_dir, dent->d_name); - - if (strcmp(addr.sun_path, drv->own_socket_path) == 0) - continue; - if (!broadcast && strstr(dent->d_name, desttxt) == NULL) - continue; - - wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s", - __func__, dent->d_name); - - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - ret = sendmsg(drv->test_socket, &msg, 0); - if (ret < 0) - perror("driver_test: sendmsg"); - count++; - } - closedir(dir); - - if (!broadcast && count == 0) { - wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found", - __func__, MAC2STR(dst)); - return -1; - } - - return ret; -} - - -static int wpa_driver_test_send_mlme(void *priv, const u8 *data, - size_t data_len, int noack) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct msghdr msg; - struct iovec io[2]; - const u8 *dest; - struct sockaddr_un addr; - struct dirent *dent; - DIR *dir; - int broadcast; - int ret = 0; - struct ieee80211_hdr *hdr; - u16 fc; - char cmd[50]; - int freq; -#ifdef HOSTAPD - char desttxt[30]; -#endif /* HOSTAPD */ - union wpa_event_data event; - - wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); - if (drv->test_socket < 0 || data_len < 10) { - wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu" - " test_dir=%p)", - __func__, drv->test_socket, - (unsigned long) data_len, - drv->test_dir); - return -1; - } - - dest = data + 4; - broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; - -#ifdef HOSTAPD - snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); -#endif /* HOSTAPD */ - - if (drv->remain_on_channel_freq) - freq = drv->remain_on_channel_freq; - else - freq = drv->current_freq; - wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz", - dbss->ifname, freq); - os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq); - io[0].iov_base = cmd; - io[0].iov_len = os_strlen(cmd); - io[1].iov_base = (void *) data; - io[1].iov_len = data_len; - - os_memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 2; - -#ifdef HOSTAPD - if (drv->test_dir == NULL) { - wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__); - return -1; - } - - dir = opendir(drv->test_dir); - if (dir == NULL) { - perror("test_driver: opendir"); - return -1; - } - while ((dent = readdir(dir))) { -#ifdef _DIRENT_HAVE_D_TYPE - /* Skip the file if it is not a socket. Also accept - * DT_UNKNOWN (0) in case the C library or underlying file - * system does not support d_type. */ - if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN) - continue; -#endif /* _DIRENT_HAVE_D_TYPE */ - if (os_strcmp(dent->d_name, ".") == 0 || - os_strcmp(dent->d_name, "..") == 0) - continue; - - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", - drv->test_dir, dent->d_name); - - if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0) - continue; - if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL) - continue; - - wpa_printf(MSG_DEBUG, "%s: Send management frame to %s", - __func__, dent->d_name); - - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - ret = sendmsg(drv->test_socket, &msg, 0); - if (ret < 0) - perror("driver_test: sendmsg(test_socket)"); - } - closedir(dir); -#else /* HOSTAPD */ - - if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || - drv->test_dir == NULL) { - if (drv->hostapd_addr_udp_set) { - msg.msg_name = &drv->hostapd_addr_udp; - msg.msg_namelen = sizeof(drv->hostapd_addr_udp); - } else { -#ifdef DRIVER_TEST_UNIX - msg.msg_name = &drv->hostapd_addr; - msg.msg_namelen = sizeof(drv->hostapd_addr); -#endif /* DRIVER_TEST_UNIX */ - } - } else if (broadcast) { - dir = opendir(drv->test_dir); - if (dir == NULL) - return -1; - while ((dent = readdir(dir))) { -#ifdef _DIRENT_HAVE_D_TYPE - /* Skip the file if it is not a socket. - * Also accept DT_UNKNOWN (0) in case - * the C library or underlying file - * system does not support d_type. */ - if (dent->d_type != DT_SOCK && - dent->d_type != DT_UNKNOWN) - continue; -#endif /* _DIRENT_HAVE_D_TYPE */ - if (os_strcmp(dent->d_name, ".") == 0 || - os_strcmp(dent->d_name, "..") == 0) - continue; - wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", - __func__, dent->d_name); - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/%s", drv->test_dir, dent->d_name); - - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - - ret = sendmsg(drv->test_socket, &msg, 0); - if (ret < 0) - perror("driver_test: sendmsg(test_socket)"); - } - closedir(dir); - return ret; - } else { - struct stat st; - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); - if (stat(addr.sun_path, &st) < 0) { - os_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s/STA-" MACSTR, - drv->test_dir, MAC2STR(dest)); - } - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - } - - if (sendmsg(drv->test_socket, &msg, 0) < 0) { - perror("sendmsg(test_socket)"); - return -1; - } -#endif /* HOSTAPD */ - - hdr = (struct ieee80211_hdr *) data; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = data; - event.tx_status.data_len = data_len; - event.tx_status.ack = ret >= 0; - wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); - -#ifdef CONFIG_P2P - if (drv->p2p && - WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { - if (drv->pending_action_tx == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " - "no pending operation"); - return ret; - } - - if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " - "unknown destination address"); - return ret; - } - - wpabuf_free(drv->pending_action_tx); - drv->pending_action_tx = NULL; - - p2p_send_action_cb(drv->p2p, drv->pending_action_freq, - drv->pending_action_dst, - drv->pending_action_src, - drv->pending_action_bssid, - ret >= 0); - } -#endif /* CONFIG_P2P */ - - return ret; -} - - -static void test_driver_scan(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - char *data) -{ - char buf[512], *pos, *end; - int ret; - struct test_driver_bss *bss; - u8 sa[ETH_ALEN]; - u8 ie[512]; - size_t ielen; - union wpa_event_data event; - - /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */ - - wpa_printf(MSG_DEBUG, "test_driver: SCAN"); - - if (*data) { - if (*data != ' ' || - hwaddr_aton(data + 1, sa)) { - wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN " - "command format"); - return; - } - - data += 18; - while (*data == ' ') - data++; - ielen = os_strlen(data) / 2; - if (ielen > sizeof(ie)) - ielen = sizeof(ie); - if (hexstr2bin(data, ie, ielen) < 0) - ielen = 0; - - wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR, - MAC2STR(sa)); - wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen); - - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = sa; - event.rx_probe_req.ie = ie; - event.rx_probe_req.ie_len = ielen; - wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); -#ifdef CONFIG_P2P - if (drv->p2p) - p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); -#endif /* CONFIG_P2P */ - } - - dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { - pos = buf; - end = buf + sizeof(buf); - - /* reply: SCANRESP BSSID SSID IEs */ - ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", - MAC2STR(bss->bssid)); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, - bss->ssid, bss->ssid_len); - ret = snprintf(pos, end - pos, " "); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen); - pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie, - bss->wps_probe_resp_ie_len); - - if (bss->privacy) { - ret = snprintf(pos, end - pos, " PRIVACY"); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - } - - sendto(drv->test_socket, buf, pos - buf, 0, - (struct sockaddr *) from, fromlen); - } -} - - -static void test_driver_assoc(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - char *data) -{ - struct test_client_socket *cli; - u8 ie[256], ssid[32]; - size_t ielen, ssid_len = 0; - char *pos, *pos2, cmd[50]; - struct test_driver_bss *bss, *tmp; - - /* data: STA-addr SSID(hex) IEs(hex) */ - - cli = os_zalloc(sizeof(*cli)); - if (cli == NULL) - return; - - if (hwaddr_aton(data, cli->addr)) { - printf("test_socket: Invalid MAC address '%s' in ASSOC\n", - data); - os_free(cli); - return; - } - pos = data + 17; - while (*pos == ' ') - pos++; - pos2 = strchr(pos, ' '); - ielen = 0; - if (pos2) { - ssid_len = (pos2 - pos) / 2; - if (hexstr2bin(pos, ssid, ssid_len) < 0) { - wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__); - os_free(cli); - return; - } - wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID", - ssid, ssid_len); - - pos = pos2 + 1; - ielen = strlen(pos) / 2; - if (ielen > sizeof(ie)) - ielen = sizeof(ie); - if (hexstr2bin(pos, ie, ielen) < 0) - ielen = 0; - } - - bss = NULL; - dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) { - if (tmp->ssid_len == ssid_len && - os_memcmp(tmp->ssid, ssid, ssid_len) == 0) { - bss = tmp; - break; - } - } - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " - "configured BSSes", __func__); - os_free(cli); - return; - } - - cli->bss = bss; - memcpy(&cli->un, from, sizeof(cli->un)); - cli->unlen = fromlen; - cli->next = drv->cli; - drv->cli = cli; - wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path", - (const u8 *) cli->un.sun_path, - cli->unlen - sizeof(cli->un.sun_family)); - - snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0", - MAC2STR(bss->bssid)); - sendto(drv->test_socket, cmd, strlen(cmd), 0, - (struct sockaddr *) from, fromlen); - - drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0); -} - - -static void test_driver_disassoc(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen) -{ - struct test_client_socket *cli; - - cli = test_driver_get_cli(drv, from, fromlen); - if (!cli) - return; - - drv_event_disassoc(drv->ctx, cli->addr); -} - - -static void test_driver_eapol(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ -#ifdef HOSTAPD - struct test_client_socket *cli; -#endif /* HOSTAPD */ - const u8 *src = NULL; - - if (datalen > 14) { - /* Skip Ethernet header */ - src = data + ETH_ALEN; - wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" - MACSTR " proto=%04x", - MAC2STR(data), MAC2STR(src), - WPA_GET_BE16(data + 2 * ETH_ALEN)); - data += 14; - datalen -= 14; - } - -#ifdef HOSTAPD - cli = test_driver_get_cli(drv, from, fromlen); - if (cli) { - drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data, - datalen); - } else { - wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " - "client"); - } -#else /* HOSTAPD */ - if (src) - drv_event_eapol_rx(drv->ctx, src, data, datalen); -#endif /* HOSTAPD */ -} - - -static void test_driver_ether(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ - struct l2_ethhdr *eth; - - if (datalen < sizeof(*eth)) - return; - - eth = (struct l2_ethhdr *) data; - wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src=" - MACSTR " proto=%04x", - MAC2STR(eth->h_dest), MAC2STR(eth->h_source), - be_to_host16(eth->h_proto)); - -#ifdef CONFIG_IEEE80211R - if (be_to_host16(eth->h_proto) == ETH_P_RRB) { - union wpa_event_data ev; - os_memset(&ev, 0, sizeof(ev)); - ev.ft_rrb_rx.src = eth->h_source; - ev.ft_rrb_rx.data = data + sizeof(*eth); - ev.ft_rrb_rx.data_len = datalen - sizeof(*eth); - } -#endif /* CONFIG_IEEE80211R */ -} - - -static void test_driver_mlme(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - int freq = 0, own_freq; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) { - size_t pos; - for (pos = 5; pos < datalen; pos++) { - if (data[pos] == ' ') - break; - } - if (pos < datalen) { - freq = atoi((const char *) &data[5]); - wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " - "freq %d MHz", bss->ifname, freq); - pos++; - data += pos; - datalen -= pos; - } - } - - if (drv->remain_on_channel_freq) - own_freq = drv->remain_on_channel_freq; - else - own_freq = drv->current_freq; - - if (freq && own_freq && freq != own_freq) { - wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " - "another frequency %d MHz (own %d MHz)", - bss->ifname, freq, own_freq); - return; - } - - hdr = (struct ieee80211_hdr *) data; - - if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) { - struct test_client_socket *cli; - cli = os_zalloc(sizeof(*cli)); - if (cli == NULL) - return; - wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR, - MAC2STR(hdr->addr2)); - memcpy(cli->addr, hdr->addr2, ETH_ALEN); - memcpy(&cli->un, from, sizeof(cli->un)); - cli->unlen = fromlen; - cli->next = drv->cli; - drv->cli = cli; - } - - wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame", - data, datalen); - fc = le_to_host16(hdr->frame_control); - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) { - wpa_printf(MSG_ERROR, "%s: received non-mgmt frame", - __func__); - return; - } - - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = data; - event.rx_mgmt.frame_len = datalen; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); -} - - -static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - char buf[2000]; - int res; - struct sockaddr_un from; - socklen_t fromlen = sizeof(from); - - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, - (struct sockaddr *) &from, &fromlen); - if (res < 0) { - perror("recvfrom(test_socket)"); - return; - } - buf[res] = '\0'; - - wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); - - if (strncmp(buf, "SCAN", 4) == 0) { - test_driver_scan(drv, &from, fromlen, buf + 4); - } else if (strncmp(buf, "ASSOC ", 6) == 0) { - test_driver_assoc(drv, &from, fromlen, buf + 6); - } else if (strcmp(buf, "DISASSOC") == 0) { - test_driver_disassoc(drv, &from, fromlen); - } else if (strncmp(buf, "EAPOL ", 6) == 0) { - test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, - res - 6); - } else if (strncmp(buf, "ETHER ", 6) == 0) { - test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6, - res - 6); - } else if (strncmp(buf, "MLME ", 5) == 0) { - test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); - } else { - wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", - (u8 *) buf, res); - } -} - - -static int test_driver_set_generic_elem(void *priv, - const u8 *elem, size_t elem_len) -{ - struct test_driver_bss *bss = priv; - - os_free(bss->ie); - - if (elem == NULL) { - bss->ie = NULL; - bss->ielen = 0; - return 0; - } - - bss->ie = os_malloc(elem_len); - if (bss->ie == NULL) { - bss->ielen = 0; - return -1; - } - - memcpy(bss->ie, elem, elem_len); - bss->ielen = elem_len; - return 0; -} - - -static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) -{ - struct test_driver_bss *bss = priv; - - if (beacon == NULL) - wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE"); - else - wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE", - beacon); - - os_free(bss->wps_beacon_ie); - - if (beacon == NULL) { - bss->wps_beacon_ie = NULL; - bss->wps_beacon_ie_len = 0; - } else { - bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon)); - if (bss->wps_beacon_ie == NULL) { - bss->wps_beacon_ie_len = 0; - return -1; - } - - os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon), - wpabuf_len(beacon)); - bss->wps_beacon_ie_len = wpabuf_len(beacon); - } - - if (proberesp == NULL) - wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS " - "IE"); - else - wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS " - "IE", proberesp); - - os_free(bss->wps_probe_resp_ie); - - if (proberesp == NULL) { - bss->wps_probe_resp_ie = NULL; - bss->wps_probe_resp_ie_len = 0; - } else { - bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp)); - if (bss->wps_probe_resp_ie == NULL) { - bss->wps_probe_resp_ie_len = 0; - return -1; - } - - os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp), - wpabuf_len(proberesp)); - bss->wps_probe_resp_ie_len = wpabuf_len(proberesp); - } - - return 0; -} - - -static int test_driver_sta_deauth(void *priv, const u8 *own_addr, - const u8 *addr, int reason) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_client_socket *cli; - - if (drv->test_socket < 0) - return -1; - - cli = drv->cli; - while (cli) { - if (memcmp(cli->addr, addr, ETH_ALEN) == 0) - break; - cli = cli->next; - } - - if (!cli) - return -1; - - return sendto(drv->test_socket, "DEAUTH", 6, 0, - (struct sockaddr *) &cli->un, cli->unlen); -} - - -static int test_driver_sta_disassoc(void *priv, const u8 *own_addr, - const u8 *addr, int reason) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_client_socket *cli; - - if (drv->test_socket < 0) - return -1; - - cli = drv->cli; - while (cli) { - if (memcmp(cli->addr, addr, ETH_ALEN) == 0) - break; - cli = cli->next; - } - - if (!cli) - return -1; - - return sendto(drv->test_socket, "DISASSOC", 8, 0, - (struct sockaddr *) &cli->un, cli->unlen); -} - - -static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid, - void *bss_ctx, void **drv_priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_driver_bss *bss; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")", - __func__, ifname, MAC2STR(bssid)); - - bss = os_zalloc(sizeof(*bss)); - if (bss == NULL) - return -1; - - bss->bss_ctx = bss_ctx; - bss->drv = drv; - os_strlcpy(bss->ifname, ifname, IFNAMSIZ); - os_memcpy(bss->bssid, bssid, ETH_ALEN); - - dl_list_add(&drv->bss, &bss->list); - if (drv->global) { - drv->global->bss_add_used = 1; - os_memcpy(drv->global->req_addr, bssid, ETH_ALEN); - } - - if (drv_priv) - *drv_priv = bss; - - return 0; -} - - -static int test_driver_bss_remove(void *priv, const char *ifname) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_driver_bss *bss; - struct test_client_socket *cli, *prev_c; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); - - dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { - if (strcmp(bss->ifname, ifname) != 0) - continue; - - for (prev_c = NULL, cli = drv->cli; cli; - prev_c = cli, cli = cli->next) { - if (cli->bss != bss) - continue; - if (prev_c) - prev_c->next = cli->next; - else - drv->cli = cli->next; - os_free(cli); - break; - } - - dl_list_del(&bss->list); - test_driver_free_bss(bss); - return 0; - } - - return -1; -} - - -static int test_driver_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) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - - wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)", - __func__, type, ifname, bss_ctx); - if (addr) - os_memcpy(if_addr, addr, ETH_ALEN); - else { - drv->alloc_iface_idx++; - if_addr[0] = 0x02; /* locally administered */ - sha1_prf(drv->own_addr, ETH_ALEN, - "hostapd test addr generation", - (const u8 *) &drv->alloc_iface_idx, - sizeof(drv->alloc_iface_idx), - if_addr + 1, ETH_ALEN - 1); - } - if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || - type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) - return test_driver_bss_add(priv, ifname, if_addr, bss_ctx, - drv_priv); - return 0; -} - - -static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type, - const char *ifname) -{ - wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); - if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || - type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) - return test_driver_bss_remove(priv, ifname); - return 0; -} - - -static int test_driver_set_ssid(void *priv, const u8 *buf, int len) -{ - struct test_driver_bss *bss = priv; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname); - if (len < 0) - return -1; - wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); - - if ((size_t) len > sizeof(bss->ssid)) - return -1; - - os_memcpy(bss->ssid, buf, len); - bss->ssid_len = len; - - return 0; -} - - -static int test_driver_set_privacy(void *priv, int enabled) -{ - struct test_driver_bss *dbss = priv; - - wpa_printf(MSG_DEBUG, "%s(enabled=%d)", __func__, enabled); - dbss->privacy = enabled; - - return 0; -} - - -static int test_driver_set_sta_vlan(void *priv, const u8 *addr, - const char *ifname, int vlan_id) -{ - wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)", - __func__, MAC2STR(addr), ifname, vlan_id); - return 0; -} - - -static int test_driver_sta_add(void *priv, - struct hostapd_sta_add_params *params) -{ - struct test_driver_bss *bss = priv; - struct wpa_driver_test_data *drv = bss->drv; - struct test_client_socket *cli; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " - "capability=0x%x listen_interval=%d)", - __func__, bss->ifname, MAC2STR(params->addr), params->aid, - params->capability, params->listen_interval); - wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", - params->supp_rates, params->supp_rates_len); - - cli = drv->cli; - while (cli) { - if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0) - break; - cli = cli->next; - } - if (!cli) { - wpa_printf(MSG_DEBUG, "%s: no matching client entry", - __func__); - return -1; - } - - cli->bss = bss; - - return 0; -} - - -static struct wpa_driver_test_data * test_alloc_data(void *ctx, - const char *ifname) -{ - struct wpa_driver_test_data *drv; - struct test_driver_bss *bss; - - drv = os_zalloc(sizeof(struct wpa_driver_test_data)); - if (drv == NULL) { - wpa_printf(MSG_ERROR, "Could not allocate memory for test " - "driver data"); - return NULL; - } - - bss = os_zalloc(sizeof(struct test_driver_bss)); - if (bss == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - wpa_trace_add_ref(drv, ctx, ctx); - dl_list_init(&drv->bss); - dl_list_add(&drv->bss, &bss->list); - os_strlcpy(bss->ifname, ifname, IFNAMSIZ); - bss->bss_ctx = ctx; - bss->drv = drv; - - /* Generate a MAC address to help testing with multiple STAs */ - drv->own_addr[0] = 0x02; /* locally administered */ - sha1_prf((const u8 *) ifname, os_strlen(ifname), - "test mac addr generation", - NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); - - return drv; -} - - -static void * test_driver_init(struct hostapd_data *hapd, - struct wpa_init_params *params) -{ - struct wpa_driver_test_data *drv; - struct sockaddr_un addr_un; - struct sockaddr_in addr_in; - struct sockaddr *addr; - socklen_t alen; - struct test_driver_bss *bss; - - drv = test_alloc_data(hapd, params->ifname); - if (drv == NULL) - return NULL; - drv->ap = 1; - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - drv->global = params->global_priv; - - bss->bss_ctx = hapd; - os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN); - os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN); - - if (params->test_socket) { - if (os_strlen(params->test_socket) >= - sizeof(addr_un.sun_path)) { - printf("Too long test_socket path\n"); - wpa_driver_test_deinit(bss); - return NULL; - } - if (strncmp(params->test_socket, "DIR:", 4) == 0) { - size_t len = strlen(params->test_socket) + 30; - drv->test_dir = os_strdup(params->test_socket + 4); - drv->own_socket_path = os_malloc(len); - if (drv->own_socket_path) { - snprintf(drv->own_socket_path, len, - "%s/AP-" MACSTR, - params->test_socket + 4, - MAC2STR(params->own_addr)); - } - } else if (strncmp(params->test_socket, "UDP:", 4) == 0) { - drv->udp_port = atoi(params->test_socket + 4); - } else { - drv->own_socket_path = os_strdup(params->test_socket); - } - if (drv->own_socket_path == NULL && drv->udp_port == 0) { - wpa_driver_test_deinit(bss); - return NULL; - } - - drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, - SOCK_DGRAM, 0); - if (drv->test_socket < 0) { - perror("socket"); - wpa_driver_test_deinit(bss); - return NULL; - } - - if (drv->udp_port) { - os_memset(&addr_in, 0, sizeof(addr_in)); - addr_in.sin_family = AF_INET; - addr_in.sin_port = htons(drv->udp_port); - addr = (struct sockaddr *) &addr_in; - alen = sizeof(addr_in); - } else { - os_memset(&addr_un, 0, sizeof(addr_un)); - addr_un.sun_family = AF_UNIX; - os_strlcpy(addr_un.sun_path, drv->own_socket_path, - sizeof(addr_un.sun_path)); - addr = (struct sockaddr *) &addr_un; - alen = sizeof(addr_un); - } - if (bind(drv->test_socket, addr, alen) < 0) { - perror("test-driver-init: bind(PF_UNIX)"); - close(drv->test_socket); - if (drv->own_socket_path) - unlink(drv->own_socket_path); - wpa_driver_test_deinit(bss); - return NULL; - } - eloop_register_read_sock(drv->test_socket, - test_driver_receive_unix, drv, NULL); - } else - drv->test_socket = -1; - - return bss; -} - - -static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - -#ifdef DRIVER_TEST_UNIX - if (drv->associated && drv->hostapd_addr_set) { - struct stat st; - if (stat(drv->hostapd_addr.sun_path, &st) < 0) { - wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s", - __func__, strerror(errno)); - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - } - } -#endif /* DRIVER_TEST_UNIX */ - - eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); -} - - -static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - if (drv->pending_p2p_scan && drv->p2p) { -#ifdef CONFIG_P2P - size_t i; - for (i = 0; i < drv->num_scanres; i++) { - struct wpa_scan_res *bss = drv->scanres[i]; - if (p2p_scan_res_handler(drv->p2p, bss->bssid, - bss->freq, bss->age, - bss->level, - (const u8 *) (bss + 1), - bss->ie_len) > 0) - return; - } - p2p_scan_res_handled(drv->p2p); -#endif /* CONFIG_P2P */ - return; - } - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - - -#ifdef DRIVER_TEST_UNIX -static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, - const char *path) -{ - struct dirent *dent; - DIR *dir; - struct sockaddr_un addr; - char cmd[512], *pos, *end; - int ret; - - dir = opendir(path); - if (dir == NULL) - return; - - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "SCAN " MACSTR, - MAC2STR(drv->own_addr)); - if (ret >= 0 && ret < end - pos) - pos += ret; - if (drv->probe_req_ie) { - ret = os_snprintf(pos, end - pos, " "); - if (ret >= 0 && ret < end - pos) - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, - drv->probe_req_ie_len); - } - if (drv->probe_req_ssid_len) { - /* Add SSID IE */ - ret = os_snprintf(pos, end - pos, "%02x%02x", - WLAN_EID_SSID, - (unsigned int) drv->probe_req_ssid_len); - if (ret >= 0 && ret < end - pos) - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid, - drv->probe_req_ssid_len); - } - end[-1] = '\0'; - - while ((dent = readdir(dir))) { - if (os_strncmp(dent->d_name, "AP-", 3) != 0 && - os_strncmp(dent->d_name, "STA-", 4) != 0) - continue; - if (drv->own_socket_path) { - size_t olen, dlen; - olen = os_strlen(drv->own_socket_path); - dlen = os_strlen(dent->d_name); - if (olen >= dlen && - os_strcmp(dent->d_name, - drv->own_socket_path + olen - dlen) == 0) - continue; - } - wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); - - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", - path, dent->d_name); - - if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0, - (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("sendto(test_socket)"); - } - } - closedir(dir); -} -#endif /* DRIVER_TEST_UNIX */ - - -static int wpa_driver_test_scan(void *priv, - struct wpa_driver_scan_params *params) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - size_t i; - - wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); - - os_free(drv->probe_req_ie); - if (params->extra_ies) { - drv->probe_req_ie = os_malloc(params->extra_ies_len); - if (drv->probe_req_ie == NULL) { - drv->probe_req_ie_len = 0; - return -1; - } - os_memcpy(drv->probe_req_ie, params->extra_ies, - params->extra_ies_len); - drv->probe_req_ie_len = params->extra_ies_len; - } else { - drv->probe_req_ie = NULL; - drv->probe_req_ie_len = 0; - } - - for (i = 0; i < params->num_ssids; i++) - wpa_hexdump(MSG_DEBUG, "Scan SSID", - params->ssids[i].ssid, params->ssids[i].ssid_len); - drv->probe_req_ssid_len = 0; - if (params->num_ssids) { - os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid, - params->ssids[0].ssid_len); - drv->probe_req_ssid_len = params->ssids[0].ssid_len; - } - wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)", - params->extra_ies, params->extra_ies_len); - - drv->num_scanres = 0; - -#ifdef DRIVER_TEST_UNIX - if (drv->test_socket >= 0 && drv->test_dir) - wpa_driver_scan_dir(drv, drv->test_dir); - - if (drv->test_socket >= 0 && drv->hostapd_addr_set && - sendto(drv->test_socket, "SCAN", 4, 0, - (struct sockaddr *) &drv->hostapd_addr, - sizeof(drv->hostapd_addr)) < 0) { - perror("sendto(test_socket)"); - } -#endif /* DRIVER_TEST_UNIX */ - - if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && - sendto(drv->test_socket, "SCAN", 4, 0, - (struct sockaddr *) &drv->hostapd_addr_udp, - sizeof(drv->hostapd_addr_udp)) < 0) { - perror("sendto(test_socket)"); - } - - eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); - eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv, - drv->ctx); - return 0; -} - - -static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct wpa_scan_results *res; - size_t i; - - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return NULL; - - res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); - if (res->res == NULL) { - os_free(res); - return NULL; - } - - for (i = 0; i < drv->num_scanres; i++) { - struct wpa_scan_res *r; - if (drv->scanres[i] == NULL) - continue; - r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len); - if (r == NULL) - break; - os_memcpy(r, drv->scanres[i], - sizeof(*r) + drv->scanres[i]->ie_len); - res->res[res->num++] = r; - } - - return res; -} - - -static int wpa_driver_test_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) -{ - wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d " - "set_tx=%d", - __func__, ifname, priv, alg, key_idx, set_tx); - if (addr) - wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); - if (seq) - wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); - if (key) - wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); - return 0; -} - - -static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap) -{ - if (ap && !drv->ap) { - wpa_driver_test_close_test_socket(drv); - wpa_driver_test_attach(drv, drv->test_dir, 1); - drv->ap = 1; - } else if (!ap && drv->ap) { - wpa_driver_test_close_test_socket(drv); - wpa_driver_test_attach(drv, drv->test_dir, 0); - drv->ap = 0; - } - - return 0; -} - - -static int wpa_driver_test_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " - "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", - __func__, priv, params->freq, params->pairwise_suite, - params->group_suite, params->key_mgmt_suite, - params->auth_alg, params->mode); - wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); - if (params->bssid) { - wpa_printf(MSG_DEBUG, " bssid=" MACSTR, - MAC2STR(params->bssid)); - } - if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " ssid", - params->ssid, params->ssid_len); - } - if (params->wpa_ie) { - wpa_hexdump(MSG_DEBUG, " wpa_ie", - params->wpa_ie, params->wpa_ie_len); - drv->assoc_wpa_ie_len = params->wpa_ie_len; - if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie)) - drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie); - os_memcpy(drv->assoc_wpa_ie, params->wpa_ie, - drv->assoc_wpa_ie_len); - } else - drv->assoc_wpa_ie_len = 0; - - wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); - - drv->ibss = params->mode == IEEE80211_MODE_IBSS; - dbss->privacy = params->key_mgmt_suite & - (WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_PSK | - WPA_KEY_MGMT_WPA_NONE | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256); - if (params->wep_key_len[params->wep_tx_keyidx]) - dbss->privacy = 1; - -#ifdef DRIVER_TEST_UNIX - if (drv->test_dir && params->bssid && - params->mode != IEEE80211_MODE_IBSS) { - os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); - drv->hostapd_addr.sun_family = AF_UNIX; - os_snprintf(drv->hostapd_addr.sun_path, - sizeof(drv->hostapd_addr.sun_path), - "%s/AP-" MACSTR, - drv->test_dir, MAC2STR(params->bssid)); - drv->hostapd_addr_set = 1; - } -#endif /* DRIVER_TEST_UNIX */ - - if (params->mode == IEEE80211_MODE_AP) { - os_memcpy(dbss->ssid, params->ssid, params->ssid_len); - dbss->ssid_len = params->ssid_len; - os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN); - if (params->wpa_ie && params->wpa_ie_len) { - dbss->ie = os_malloc(params->wpa_ie_len); - if (dbss->ie) { - os_memcpy(dbss->ie, params->wpa_ie, - params->wpa_ie_len); - dbss->ielen = params->wpa_ie_len; - } - } - } else if (drv->test_socket >= 0 && - (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { - char cmd[200], *pos, *end; - int ret; - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ", - MAC2STR(drv->own_addr)); - if (ret >= 0 && ret < end - pos) - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, params->ssid, - params->ssid_len); - ret = os_snprintf(pos, end - pos, " "); - if (ret >= 0 && ret < end - pos) - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie, - params->wpa_ie_len); - end[-1] = '\0'; -#ifdef DRIVER_TEST_UNIX - if (drv->hostapd_addr_set && - sendto(drv->test_socket, cmd, os_strlen(cmd), 0, - (struct sockaddr *) &drv->hostapd_addr, - sizeof(drv->hostapd_addr)) < 0) { - perror("sendto(test_socket)"); - return -1; - } -#endif /* DRIVER_TEST_UNIX */ - if (drv->hostapd_addr_udp_set && - sendto(drv->test_socket, cmd, os_strlen(cmd), 0, - (struct sockaddr *) &drv->hostapd_addr_udp, - sizeof(drv->hostapd_addr_udp)) < 0) { - perror("sendto(test_socket)"); - return -1; - } - - os_memcpy(dbss->ssid, params->ssid, params->ssid_len); - dbss->ssid_len = params->ssid_len; - } else { - drv->associated = 1; - if (params->mode == IEEE80211_MODE_IBSS) { - os_memcpy(dbss->ssid, params->ssid, params->ssid_len); - dbss->ssid_len = params->ssid_len; - if (params->bssid) - os_memcpy(dbss->bssid, params->bssid, - ETH_ALEN); - else { - os_get_random(dbss->bssid, ETH_ALEN); - dbss->bssid[0] &= ~0x01; - dbss->bssid[0] |= 0x02; - } - } - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); - } - - return 0; -} - - -static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) -{ - struct test_driver_bss *dbss = priv; - os_memcpy(bssid, dbss->bssid, ETH_ALEN); - return 0; -} - - -static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) -{ - struct test_driver_bss *dbss = priv; - os_memcpy(ssid, dbss->ssid, 32); - return dbss->ssid_len; -} - - -static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) -{ -#ifdef DRIVER_TEST_UNIX - if (drv->test_socket >= 0 && - sendto(drv->test_socket, "DISASSOC", 8, 0, - (struct sockaddr *) &drv->hostapd_addr, - sizeof(drv->hostapd_addr)) < 0) { - perror("sendto(test_socket)"); - return -1; - } -#endif /* DRIVER_TEST_UNIX */ - if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && - sendto(drv->test_socket, "DISASSOC", 8, 0, - (struct sockaddr *) &drv->hostapd_addr_udp, - sizeof(drv->hostapd_addr_udp)) < 0) { - perror("sendto(test_socket)"); - return -1; - } - return 0; -} - - -static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - os_memset(dbss->bssid, 0, ETH_ALEN); - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - return wpa_driver_test_send_disassoc(drv); -} - - -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 void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen, - const char *data) -{ - struct wpa_scan_res *res; - const char *pos, *pos2; - size_t len; - u8 *ie_pos, *ie_start, *ie_end; -#define MAX_IE_LEN 1000 - const u8 *ds_params; - - wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); - if (drv->num_scanres >= MAX_SCAN_RESULTS) { - wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan " - "result"); - return; - } - - /* SCANRESP BSSID SSID IEs */ - - res = os_zalloc(sizeof(*res) + MAX_IE_LEN); - if (res == NULL) - return; - ie_start = ie_pos = (u8 *) (res + 1); - ie_end = ie_pos + MAX_IE_LEN; - - if (hwaddr_aton(data, res->bssid)) { - wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres"); - os_free(res); - return; - } - - pos = data + 17; - while (*pos == ' ') - pos++; - pos2 = os_strchr(pos, ' '); - if (pos2 == NULL) { - wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination " - "in scanres"); - os_free(res); - return; - } - len = (pos2 - pos) / 2; - if (len > 32) - len = 32; - /* - * Generate SSID IE from the SSID field since this IE is not included - * in the main IE field. - */ - *ie_pos++ = WLAN_EID_SSID; - *ie_pos++ = len; - if (hexstr2bin(pos, ie_pos, len) < 0) { - wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres"); - os_free(res); - return; - } - ie_pos += len; - - pos = pos2 + 1; - pos2 = os_strchr(pos, ' '); - if (pos2 == NULL) - len = os_strlen(pos) / 2; - else - len = (pos2 - pos) / 2; - if ((int) len > ie_end - ie_pos) - len = ie_end - ie_pos; - if (hexstr2bin(pos, ie_pos, len) < 0) { - wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres"); - os_free(res); - return; - } - ie_pos += len; - res->ie_len = ie_pos - ie_start; - - if (pos2) { - pos = pos2 + 1; - while (*pos == ' ') - pos++; - if (os_strstr(pos, "PRIVACY")) - res->caps |= IEEE80211_CAP_PRIVACY; - if (os_strstr(pos, "IBSS")) - res->caps |= IEEE80211_CAP_IBSS; - } - - ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS); - if (ds_params && ds_params[1] > 0) { - if (ds_params[2] >= 1 && ds_params[2] <= 13) - res->freq = 2407 + ds_params[2] * 5; - } - - os_free(drv->scanres[drv->num_scanres]); - drv->scanres[drv->num_scanres++] = res; -} - - -static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen, - const char *data) -{ - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - /* ASSOCRESP BSSID */ - if (hwaddr_aton(data, bss->bssid)) { - wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " - "assocresp"); - } - if (drv->use_associnfo) { - union wpa_event_data event; - os_memset(&event, 0, sizeof(event)); - event.assoc_info.req_ies = drv->assoc_wpa_ie; - event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len; - wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event); - } - drv->associated = 1; - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); -} - - -static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen) -{ - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); -} - - -static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen, - const u8 *data, size_t data_len) -{ - const u8 *src; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - if (data_len > 14) { - /* Skip Ethernet header */ - src = data + ETH_ALEN; - data += 14; - data_len -= 14; - } else - src = bss->bssid; - - drv_event_eapol_rx(drv->ctx, src, data, data_len); -} - - -static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen, - const u8 *data, size_t data_len) -{ - int freq = 0, own_freq; - union wpa_event_data event; - const struct ieee80211_mgmt *mgmt; - u16 fc; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) { - size_t pos; - for (pos = 5; pos < data_len; pos++) { - if (data[pos] == ' ') - break; - } - if (pos < data_len) { - freq = atoi((const char *) &data[5]); - wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " - "freq %d MHz", bss->ifname, freq); - pos++; - data += pos; - data_len -= pos; - } - } - - if (drv->remain_on_channel_freq) - own_freq = drv->remain_on_channel_freq; - else - own_freq = drv->current_freq; - - if (freq && own_freq && freq != own_freq) { - wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " - "another frequency %d MHz (own %d MHz)", - bss->ifname, freq, own_freq); - return; - } - - os_memset(&event, 0, sizeof(event)); - event.mlme_rx.buf = data; - event.mlme_rx.len = data_len; - event.mlme_rx.freq = freq; - wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event); - - mgmt = (const struct ieee80211_mgmt *) data; - fc = le_to_host16(mgmt->frame_control); - - if (drv->probe_req_report && data_len >= 24) { - if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) { - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - data_len - (mgmt->u.probe_req.variable - data); - wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, - &event); -#ifdef CONFIG_P2P - if (drv->p2p) - p2p_probe_req_rx(drv->p2p, mgmt->sa, - mgmt->da, mgmt->bssid, - event.rx_probe_req.ie, - event.rx_probe_req.ie_len); -#endif /* CONFIG_P2P */ - } - } - -#ifdef CONFIG_P2P - if (drv->p2p && - WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { - size_t hdr_len; - hdr_len = (const u8 *) - &mgmt->u.action.u.vs_public_action.action - data; - p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid, - mgmt->u.action.category, - &mgmt->u.action.u.vs_public_action.action, - data_len - hdr_len, freq); - } -#endif /* CONFIG_P2P */ - -} - - -static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen, - const u8 *data, size_t data_len) -{ - char buf[512], *pos, *end; - int ret; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ -#ifdef CONFIG_P2P - if (drv->probe_req_report && drv->p2p && data_len) { - const char *d = (const char *) data; - u8 sa[ETH_ALEN]; - u8 ie[512]; - size_t ielen; - - if (hwaddr_aton(d, sa)) - return; - d += 18; - while (*d == ' ') - d++; - ielen = os_strlen(d) / 2; - if (ielen > sizeof(ie)) - ielen = sizeof(ie); - if (hexstr2bin(d, ie, ielen) < 0) - ielen = 0; - drv->probe_from = from; - drv->probe_from_len = fromlen; - p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); - drv->probe_from = NULL; - } -#endif /* CONFIG_P2P */ - - if (!drv->ibss) - return; - - pos = buf; - end = buf + sizeof(buf); - - /* reply: SCANRESP BSSID SSID IEs */ - ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", - MAC2STR(bss->bssid)); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, - bss->ssid, bss->ssid_len); - ret = snprintf(pos, end - pos, " "); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie, - drv->assoc_wpa_ie_len); - - if (bss->privacy) { - ret = snprintf(pos, end - pos, " PRIVACY"); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - } - - ret = snprintf(pos, end - pos, " IBSS"); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - - sendto(drv->test_socket, buf, pos - buf, 0, - (struct sockaddr *) from, fromlen); -} - - -static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, - void *sock_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - char *buf; - int res; - struct sockaddr_storage from; - socklen_t fromlen = sizeof(from); - const size_t buflen = 2000; - - if (drv->ap) { - test_driver_receive_unix(sock, eloop_ctx, sock_ctx); - return; - } - - buf = os_malloc(buflen); - if (buf == NULL) - return; - res = recvfrom(sock, buf, buflen - 1, 0, - (struct sockaddr *) &from, &fromlen); - if (res < 0) { - perror("recvfrom(test_socket)"); - os_free(buf); - return; - } - buf[res] = '\0'; - - wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); - - if (os_strncmp(buf, "SCANRESP ", 9) == 0) { - wpa_driver_test_scanresp(drv, (struct sockaddr *) &from, - fromlen, buf + 9); - } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) { - wpa_driver_test_assocresp(drv, (struct sockaddr *) &from, - fromlen, buf + 10); - } else if (os_strcmp(buf, "DISASSOC") == 0) { - wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, - fromlen); - } else if (os_strcmp(buf, "DEAUTH") == 0) { - wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, - fromlen); - } else if (os_strncmp(buf, "EAPOL ", 6) == 0) { - wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen, - (const u8 *) buf + 6, res - 6); - } else if (os_strncmp(buf, "MLME ", 5) == 0) { - wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, - (const u8 *) buf + 5, res - 5); - } else if (os_strncmp(buf, "SCAN ", 5) == 0) { - wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from, - fromlen, - (const u8 *) buf + 5, res - 5); - } else { - wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", - (u8 *) buf, res); - } - os_free(buf); -} - - -static void * wpa_driver_test_init2(void *ctx, const char *ifname, - void *global_priv) -{ - struct wpa_driver_test_data *drv; - struct wpa_driver_test_global *global = global_priv; - struct test_driver_bss *bss; - - drv = test_alloc_data(ctx, ifname); - if (drv == NULL) - return NULL; - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - drv->global = global_priv; - drv->test_socket = -1; - - /* Set dummy BSSID and SSID for testing. */ - bss->bssid[0] = 0x02; - bss->bssid[1] = 0x00; - bss->bssid[2] = 0x00; - bss->bssid[3] = 0x00; - bss->bssid[4] = 0x00; - bss->bssid[5] = 0x01; - os_memcpy(bss->ssid, "test", 5); - bss->ssid_len = 4; - - if (global->bss_add_used) { - os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN); - global->bss_add_used = 0; - } - - eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); - - return bss; -} - - -static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) -{ - if (drv->test_socket >= 0) { - eloop_unregister_read_sock(drv->test_socket); - close(drv->test_socket); - drv->test_socket = -1; - } - - if (drv->own_socket_path) { - unlink(drv->own_socket_path); - os_free(drv->own_socket_path); - drv->own_socket_path = NULL; - } -} - - -static void wpa_driver_test_deinit(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_client_socket *cli, *prev; - int i; - -#ifdef CONFIG_P2P - if (drv->p2p) - p2p_deinit(drv->p2p); - wpabuf_free(drv->pending_action_tx); -#endif /* CONFIG_P2P */ - - cli = drv->cli; - while (cli) { - prev = cli; - cli = cli->next; - os_free(prev); - } - -#ifdef HOSTAPD - /* There should be only one BSS remaining at this point. */ - if (dl_list_len(&drv->bss) != 1) - wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries", - __func__, dl_list_len(&drv->bss)); -#endif /* HOSTAPD */ - - test_driver_free_bsses(drv); - - wpa_driver_test_close_test_socket(drv); - eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); - eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); - eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); - os_free(drv->test_dir); - for (i = 0; i < MAX_SCAN_RESULTS; i++) - os_free(drv->scanres[i]); - os_free(drv->probe_req_ie); - wpa_trace_remove_ref(drv, ctx, drv->ctx); - os_free(drv); -} - - -static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, - const char *dir, int ap) -{ -#ifdef DRIVER_TEST_UNIX - static unsigned int counter = 0; - struct sockaddr_un addr; - size_t len; - - os_free(drv->own_socket_path); - if (dir) { - len = os_strlen(dir) + 30; - drv->own_socket_path = os_malloc(len); - if (drv->own_socket_path == NULL) - return -1; - os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR, - dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr)); - } else { - drv->own_socket_path = os_malloc(100); - if (drv->own_socket_path == NULL) - return -1; - os_snprintf(drv->own_socket_path, 100, - "/tmp/wpa_supplicant_test-%d-%d", - getpid(), counter++); - } - - drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); - if (drv->test_socket < 0) { - perror("socket(PF_UNIX)"); - os_free(drv->own_socket_path); - drv->own_socket_path = NULL; - return -1; - } - - os_memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); - if (bind(drv->test_socket, (struct sockaddr *) &addr, - sizeof(addr)) < 0) { - perror("test-driver-attach: bind(PF_UNIX)"); - close(drv->test_socket); - unlink(drv->own_socket_path); - os_free(drv->own_socket_path); - drv->own_socket_path = NULL; - return -1; - } - - eloop_register_read_sock(drv->test_socket, - wpa_driver_test_receive_unix, drv, NULL); - - return 0; -#else /* DRIVER_TEST_UNIX */ - return -1; -#endif /* DRIVER_TEST_UNIX */ -} - - -static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, - char *dst) -{ - char *pos; - - pos = os_strchr(dst, ':'); - if (pos == NULL) - return -1; - *pos++ = '\0'; - wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos); - - drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->test_socket < 0) { - perror("socket(PF_INET)"); - return -1; - } - - os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp)); - drv->hostapd_addr_udp.sin_family = AF_INET; -#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) - { - int a[4]; - u8 *pos; - sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); - pos = (u8 *) &drv->hostapd_addr_udp.sin_addr; - *pos++ = a[0]; - *pos++ = a[1]; - *pos++ = a[2]; - *pos++ = a[3]; - } -#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - inet_aton(dst, &drv->hostapd_addr_udp.sin_addr); -#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - drv->hostapd_addr_udp.sin_port = htons(atoi(pos)); - - drv->hostapd_addr_udp_set = 1; - - eloop_register_read_sock(drv->test_socket, - wpa_driver_test_receive_unix, drv, NULL); - - return 0; -} - - -static int wpa_driver_test_set_param(void *priv, const char *param) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - const char *pos; - - wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); - if (param == NULL) - return 0; - - wpa_driver_test_close_test_socket(drv); - -#ifdef DRIVER_TEST_UNIX - pos = os_strstr(param, "test_socket="); - if (pos) { - const char *pos2; - size_t len; - - pos += 12; - pos2 = os_strchr(pos, ' '); - if (pos2) - len = pos2 - pos; - else - len = os_strlen(pos); - if (len > sizeof(drv->hostapd_addr.sun_path)) - return -1; - os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); - drv->hostapd_addr.sun_family = AF_UNIX; - os_memcpy(drv->hostapd_addr.sun_path, pos, len); - drv->hostapd_addr_set = 1; - } -#endif /* DRIVER_TEST_UNIX */ - - pos = os_strstr(param, "test_dir="); - if (pos) { - char *end; - os_free(drv->test_dir); - drv->test_dir = os_strdup(pos + 9); - if (drv->test_dir == NULL) - return -1; - end = os_strchr(drv->test_dir, ' '); - if (end) - *end = '\0'; - if (wpa_driver_test_attach(drv, drv->test_dir, 0)) - return -1; - } else { - pos = os_strstr(param, "test_udp="); - if (pos) { - char *dst, *epos; - dst = os_strdup(pos + 9); - if (dst == NULL) - return -1; - epos = os_strchr(dst, ' '); - if (epos) - *epos = '\0'; - if (wpa_driver_test_attach_udp(drv, dst)) - return -1; - os_free(dst); - } else if (wpa_driver_test_attach(drv, NULL, 0)) - return -1; - } - - if (os_strstr(param, "use_associnfo=1")) { - wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); - drv->use_associnfo = 1; - } - - if (os_strstr(param, "p2p_mgmt=1")) { - wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P " - "management"); - if (wpa_driver_test_init_p2p(drv) < 0) - return -1; - } - - return 0; -} - - -static const u8 * wpa_driver_test_get_mac_addr(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s", __func__); - return drv->own_addr; -} - - -static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, - const u8 *data, size_t data_len) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - char *msg; - size_t msg_len; - struct l2_ethhdr eth; - struct sockaddr *addr; - socklen_t alen; -#ifdef DRIVER_TEST_UNIX - struct sockaddr_un addr_un; -#endif /* DRIVER_TEST_UNIX */ - - wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len); - - os_memset(ð, 0, sizeof(eth)); - os_memcpy(eth.h_dest, dest, ETH_ALEN); - os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN); - eth.h_proto = host_to_be16(proto); - - msg_len = 6 + sizeof(eth) + data_len; - msg = os_malloc(msg_len); - if (msg == NULL) - return -1; - os_memcpy(msg, "EAPOL ", 6); - os_memcpy(msg + 6, ð, sizeof(eth)); - os_memcpy(msg + 6 + sizeof(eth), data, data_len); - - if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || - drv->test_dir == NULL) { - if (drv->hostapd_addr_udp_set) { - addr = (struct sockaddr *) &drv->hostapd_addr_udp; - alen = sizeof(drv->hostapd_addr_udp); - } else { -#ifdef DRIVER_TEST_UNIX - addr = (struct sockaddr *) &drv->hostapd_addr; - alen = sizeof(drv->hostapd_addr); -#else /* DRIVER_TEST_UNIX */ - os_free(msg); - return -1; -#endif /* DRIVER_TEST_UNIX */ - } - } else { -#ifdef DRIVER_TEST_UNIX - struct stat st; - os_memset(&addr_un, 0, sizeof(addr_un)); - addr_un.sun_family = AF_UNIX; - os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), - "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); - if (stat(addr_un.sun_path, &st) < 0) { - os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), - "%s/AP-" MACSTR, - drv->test_dir, MAC2STR(dest)); - } - addr = (struct sockaddr *) &addr_un; - alen = sizeof(addr_un); -#else /* DRIVER_TEST_UNIX */ - os_free(msg); - return -1; -#endif /* DRIVER_TEST_UNIX */ - } - - if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) { - perror("sendmsg(test_socket)"); - os_free(msg); - return -1; - } - - os_free(msg); - return 0; -} - - -static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - os_memset(capa, 0, sizeof(*capa)); - capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE | - WPA_DRIVER_CAPA_KEY_MGMT_FT | - WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; - capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | - WPA_DRIVER_CAPA_ENC_WEP104 | - WPA_DRIVER_CAPA_ENC_TKIP | - WPA_DRIVER_CAPA_ENC_CCMP; - capa->auth = WPA_DRIVER_AUTH_OPEN | - WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - if (drv->p2p) - capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT; - capa->flags |= WPA_DRIVER_FLAGS_AP; - capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; - capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; - capa->max_scan_ssids = 2; - capa->max_remain_on_chan = 60000; - - return 0; -} - - -static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, - int protect_type, - int key_type) -{ - wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d", - __func__, protect_type, key_type); - - if (addr) { - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, - __func__, MAC2STR(addr)); - } - - return 0; -} - - -static void * wpa_driver_test_global_init(void) -{ - struct wpa_driver_test_global *global; - - global = os_zalloc(sizeof(*global)); - return global; -} - - -static void wpa_driver_test_global_deinit(void *priv) -{ - struct wpa_driver_test_global *global = priv; - os_free(global); -} - - -static struct wpa_interface_info * -wpa_driver_test_get_interfaces(void *global_priv) -{ - /* struct wpa_driver_test_global *global = priv; */ - struct wpa_interface_info *iface; - - iface = os_zalloc(sizeof(*iface)); - if (iface == NULL) - return iface; - iface->ifname = os_strdup("sta0"); - iface->desc = os_strdup("test interface 0"); - iface->drv_name = "test"; - iface->next = os_zalloc(sizeof(*iface)); - if (iface->next) { - iface->next->ifname = os_strdup("sta1"); - iface->next->desc = os_strdup("test interface 1"); - iface->next->drv_name = "test"; - } - - return iface; -} - - -static struct hostapd_hw_modes * -wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) -{ - struct hostapd_hw_modes *modes; - size_t i; - - *num_modes = 3; - *flags = 0; - modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes)); - if (modes == NULL) - return NULL; - modes[0].mode = HOSTAPD_MODE_IEEE80211G; - modes[0].num_channels = 11; - modes[0].num_rates = 12; - modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); - modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int)); - if (modes[0].channels == NULL || modes[0].rates == NULL) - goto fail; - for (i = 0; i < 11; i++) { - modes[0].channels[i].chan = i + 1; - modes[0].channels[i].freq = 2412 + 5 * i; - modes[0].channels[i].flag = 0; - } - modes[0].rates[0] = 10; - modes[0].rates[1] = 20; - modes[0].rates[2] = 55; - modes[0].rates[3] = 110; - modes[0].rates[4] = 60; - modes[0].rates[5] = 90; - modes[0].rates[6] = 120; - modes[0].rates[7] = 180; - modes[0].rates[8] = 240; - modes[0].rates[9] = 360; - modes[0].rates[10] = 480; - modes[0].rates[11] = 540; - - modes[1].mode = HOSTAPD_MODE_IEEE80211B; - modes[1].num_channels = 11; - modes[1].num_rates = 4; - modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); - modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int)); - if (modes[1].channels == NULL || modes[1].rates == NULL) - goto fail; - for (i = 0; i < 11; i++) { - modes[1].channels[i].chan = i + 1; - modes[1].channels[i].freq = 2412 + 5 * i; - modes[1].channels[i].flag = 0; - } - modes[1].rates[0] = 10; - modes[1].rates[1] = 20; - modes[1].rates[2] = 55; - modes[1].rates[3] = 110; - - modes[2].mode = HOSTAPD_MODE_IEEE80211A; - modes[2].num_channels = 1; - modes[2].num_rates = 8; - modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data)); - modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int)); - if (modes[2].channels == NULL || modes[2].rates == NULL) - goto fail; - modes[2].channels[0].chan = 60; - modes[2].channels[0].freq = 5300; - modes[2].channels[0].flag = 0; - modes[2].rates[0] = 60; - modes[2].rates[1] = 90; - modes[2].rates[2] = 120; - modes[2].rates[3] = 180; - modes[2].rates[4] = 240; - modes[2].rates[5] = 360; - modes[2].rates[6] = 480; - modes[2].rates[7] = 540; - - return modes; - -fail: - if (modes) { - for (i = 0; i < *num_modes; i++) { - os_free(modes[i].channels); - os_free(modes[i].rates); - } - os_free(modes); - } - return NULL; -} - - -static int wpa_driver_test_set_freq(void *priv, - struct hostapd_freq_params *freq) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq); - drv->current_freq = freq->freq; - return 0; -} - - -static int wpa_driver_test_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) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - int ret = -1; - u8 *buf; - struct ieee80211_hdr *hdr; - - wpa_printf(MSG_DEBUG, "test: Send Action frame"); - - if ((drv->remain_on_channel_freq && - freq != drv->remain_on_channel_freq) || - (drv->remain_on_channel_freq == 0 && - freq != (unsigned int) drv->current_freq)) { - wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on " - "unexpected channel: freq=%u MHz (current_freq=%u " - "MHz, remain-on-channel freq=%u MHz)", - freq, drv->current_freq, - drv->remain_on_channel_freq); - return -1; - } - - buf = os_zalloc(24 + data_len); - if (buf == NULL) - return ret; - os_memcpy(buf + 24, data, data_len); - hdr = (struct ieee80211_hdr *) buf; - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); - os_memcpy(hdr->addr1, dst, ETH_ALEN); - os_memcpy(hdr->addr2, src, ETH_ALEN); - os_memcpy(hdr->addr3, bssid, ETH_ALEN); - - ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0); - os_free(buf); - return ret; -} - - -#ifdef CONFIG_P2P -static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - - if (drv->pending_action_tx == NULL) - return; - - if (drv->off_channel_freq != drv->pending_action_freq) { - wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " - "waiting for another freq=%u", - drv->pending_action_freq); - return; - } - wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " - MACSTR, MAC2STR(drv->pending_action_dst)); - wpa_driver_test_send_action(drv, drv->pending_action_freq, 0, - drv->pending_action_dst, - drv->pending_action_src, - drv->pending_action_bssid, - wpabuf_head(drv->pending_action_tx), - wpabuf_len(drv->pending_action_tx), - drv->pending_action_no_cck); -} -#endif /* CONFIG_P2P */ - - -static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout"); - - os_memset(&data, 0, sizeof(data)); - data.remain_on_channel.freq = drv->remain_on_channel_freq; - data.remain_on_channel.duration = drv->remain_on_channel_duration; - - if (drv->p2p) - drv->off_channel_freq = 0; - - drv->remain_on_channel_freq = 0; - - wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); -} - - -static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, - unsigned int duration) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)", - __func__, freq, duration); - if (drv->remain_on_channel_freq && - drv->remain_on_channel_freq != freq) { - wpa_printf(MSG_DEBUG, "test: Refuse concurrent " - "remain_on_channel request"); - return -1; - } - - drv->remain_on_channel_freq = freq; - drv->remain_on_channel_duration = duration; - eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); - eloop_register_timeout(duration / 1000, (duration % 1000) * 1000, - test_remain_on_channel_timeout, drv, NULL); - - os_memset(&data, 0, sizeof(data)); - data.remain_on_channel.freq = freq; - data.remain_on_channel.duration = duration; - wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); - -#ifdef CONFIG_P2P - if (drv->p2p) { - drv->off_channel_freq = drv->remain_on_channel_freq; - test_send_action_cb(drv, NULL); - if (drv->off_channel_freq == drv->pending_listen_freq) { - p2p_listen_cb(drv->p2p, drv->pending_listen_freq, - drv->pending_listen_duration); - drv->pending_listen_freq = 0; - } - } -#endif /* CONFIG_P2P */ - - return 0; -} - - -static int wpa_driver_test_cancel_remain_on_channel(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->remain_on_channel_freq) - return -1; - drv->remain_on_channel_freq = 0; - eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); - return 0; -} - - -static int wpa_driver_test_probe_req_report(void *priv, int report) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report); - drv->probe_req_report = report; - return 0; -} - - -#ifdef CONFIG_P2P - -static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); - if (!drv->p2p) - return -1; - return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0); -} - - -static int wpa_driver_test_p2p_stop_find(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->p2p) - return -1; - p2p_stop_find(drv->p2p); - return 0; -} - - -static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); - if (!drv->p2p) - return -1; - return p2p_listen(drv->p2p, timeout); -} - - -static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, - int wps_method, int go_intent, - const u8 *own_interface_addr, - unsigned int force_freq, - int persistent_group) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d " - "go_intent=%d " - "own_interface_addr=" MACSTR " force_freq=%u " - "persistent_group=%d)", - __func__, MAC2STR(peer_addr), wps_method, go_intent, - MAC2STR(own_interface_addr), force_freq, persistent_group); - if (!drv->p2p) - return -1; - return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent, - own_interface_addr, force_freq, persistent_group, - NULL, 0, 0, 0); -} - - -static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")", - __func__, MAC2STR(peer_addr)); - if (!drv->p2p) - return -1; - p2p_wps_success_cb(drv->p2p, peer_addr); - return 0; -} - - -static int wpa_driver_test_p2p_group_formation_failed(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->p2p) - return -1; - p2p_group_formation_failed(drv->p2p); - return 0; -} - - -static int wpa_driver_test_p2p_set_params(void *priv, - const struct p2p_params *params) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->p2p) - return -1; - if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 || - p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 || - p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type, - params->num_sec_dev_types) < 0) - return -1; - return 0; -} - - -static int test_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) -{ - struct wpa_driver_test_data *drv = ctx; - struct wpa_driver_scan_params params; - int ret; - struct wpabuf *wps_ie, *ies; - int social_channels[] = { 2412, 2437, 2462, 0, 0 }; - size_t ielen; - - wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)", - __func__, type, freq); - - os_memset(¶ms, 0, sizeof(params)); - - /* P2P Wildcard SSID */ - params.num_ssids = 1; - params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; - params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; - -#if 0 /* TODO: WPS IE */ - wpa_s->wps->dev.p2p = 1; - wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, - wpa_s->wps->uuid, WPS_REQ_ENROLLEE); -#else - wps_ie = wpabuf_alloc(1); -#endif - if (wps_ie == NULL) - return -1; - - ielen = p2p_scan_ie_buf_len(drv->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { - wpabuf_free(wps_ie); - return -1; - } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); - - p2p_scan_ie(drv->p2p, ies, dev_id); - - params.extra_ies = wpabuf_head(ies); - params.extra_ies_len = wpabuf_len(ies); - - switch (type) { - case P2P_SCAN_SOCIAL: - params.freqs = social_channels; - break; - case P2P_SCAN_FULL: - break; - case P2P_SCAN_SOCIAL_PLUS_ONE: - social_channels[3] = freq; - params.freqs = social_channels; - break; - } - - drv->pending_p2p_scan = 1; - ret = wpa_driver_test_scan(drv, ¶ms); - - wpabuf_free(ies); - - return ret; -} - - -static int test_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) -{ - struct wpa_driver_test_data *drv = ctx; - - wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR - " bssid=" MACSTR " len=%d", - __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), - (int) len); - if (freq <= 0) { - wpa_printf(MSG_WARNING, "P2P: No frequency specified for " - "action frame TX"); - return -1; - } - - if (drv->pending_action_tx) { - wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " - "to " MACSTR, MAC2STR(drv->pending_action_dst)); - wpabuf_free(drv->pending_action_tx); - } - drv->pending_action_tx = wpabuf_alloc(len); - if (drv->pending_action_tx == NULL) - return -1; - wpabuf_put_data(drv->pending_action_tx, buf, len); - os_memcpy(drv->pending_action_src, src, ETH_ALEN); - os_memcpy(drv->pending_action_dst, dst, ETH_ALEN); - os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN); - drv->pending_action_freq = freq; - drv->pending_action_no_cck = 1; - - if (drv->off_channel_freq == freq) { - /* Already on requested channel; send immediately */ - /* TODO: Would there ever be need to extend the current - * duration on the channel? */ - eloop_cancel_timeout(test_send_action_cb, drv, NULL); - eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL); - return 0; - } - - wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " - "once the driver gets to the requested channel"); - if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) { - wpa_printf(MSG_DEBUG, "P2P: Failed to request driver " - "to remain on channel (%u MHz) for Action " - "Frame TX", freq); - return -1; - } - - return 0; -} - - -static void test_send_action_done(void *ctx) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) -{ - struct wpa_driver_test_data *drv = ctx; - union wpa_event_data event; - wpa_printf(MSG_DEBUG, "%s", __func__); - os_memset(&event, 0, sizeof(event)); - event.p2p_go_neg_completed.res = res; - wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event); -} - - -static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) -{ - struct wpa_driver_test_data *drv = ctx; - union wpa_event_data event; - wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src)); - os_memset(&event, 0, sizeof(event)); - event.p2p_go_neg_req_rx.src = src; - event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id; - wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event); -} - - -static void test_dev_found(void *ctx, const u8 *addr, - const struct p2p_peer_info *info, int new_device) -{ - struct wpa_driver_test_data *drv = ctx; - union wpa_event_data event; - char devtype[WPS_DEV_TYPE_BUFSIZE]; - wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR - " pri_dev_type=%s name='%s' config_methods=0x%x " - "dev_capab=0x%x group_capab=0x%x)", - __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr), - wps_dev_type_bin2str(info->pri_dev_type, devtype, - sizeof(devtype)), - info->device_name, info->config_methods, info->dev_capab, - info->group_capab); - - os_memset(&event, 0, sizeof(event)); - event.p2p_dev_found.addr = addr; - event.p2p_dev_found.dev_addr = info->p2p_device_addr; - event.p2p_dev_found.pri_dev_type = info->pri_dev_type; - event.p2p_dev_found.dev_name = info->device_name; - event.p2p_dev_found.config_methods = info->config_methods; - event.p2p_dev_found.dev_capab = info->dev_capab; - event.p2p_dev_found.group_capab = info->group_capab; - wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event); -} - - -static int test_start_listen(void *ctx, unsigned int freq, - unsigned int duration, - const struct wpabuf *probe_resp_ie) -{ - struct wpa_driver_test_data *drv = ctx; - - wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)", - __func__, freq, duration); - - if (wpa_driver_test_probe_req_report(drv, 1) < 0) - return -1; - - drv->pending_listen_freq = freq; - drv->pending_listen_duration = duration; - - if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) { - drv->pending_listen_freq = 0; - return -1; - } - - return 0; -} - - -static void test_stop_listen(void *ctx) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static int test_send_probe_resp(void *ctx, const struct wpabuf *buf) -{ - struct wpa_driver_test_data *drv = ctx; - char resp[512], *pos, *end; - int ret; - const struct ieee80211_mgmt *mgmt; - const u8 *ie, *ie_end; - - wpa_printf(MSG_DEBUG, "%s", __func__); - wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf); - if (wpabuf_len(buf) < 24) - return -1; - if (!drv->probe_from) { - wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__); - return -1; - } - - pos = resp; - end = resp + sizeof(resp); - - mgmt = wpabuf_head(buf); - - /* reply: SCANRESP BSSID SSID IEs */ - ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ", - MAC2STR(mgmt->bssid)); - if (ret < 0 || ret >= end - pos) - return -1; - pos += ret; - - ie = mgmt->u.probe_resp.variable; - ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf); - if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID || - ie + 2 + ie[1] > ie_end) - return -1; - pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]); - - ret = os_snprintf(pos, end - pos, " "); - if (ret < 0 || ret >= end - pos) - return -1; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie); - - sendto(drv->test_socket, resp, pos - resp, 0, - drv->probe_from, drv->probe_from_len); - - return 0; -} - - -static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len) -{ - wpa_printf(MSG_DEBUG, "%s", __func__); - /* TODO */ -} - - -static void test_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) -{ - wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", - __func__, MAC2STR(peer), config_methods); - /* TODO */ -} - - -static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) -{ - wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", - __func__, MAC2STR(peer), config_methods); - /* TODO */ -} - -#endif /* CONFIG_P2P */ - - -static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv) -{ -#ifdef CONFIG_P2P - struct p2p_config p2p; - unsigned int r; - int i; - - os_memset(&p2p, 0, sizeof(p2p)); - p2p.msg_ctx = drv->ctx; - p2p.cb_ctx = drv; - p2p.p2p_scan = test_p2p_scan; - p2p.send_action = test_send_action; - p2p.send_action_done = test_send_action_done; - p2p.go_neg_completed = test_go_neg_completed; - p2p.go_neg_req_rx = test_go_neg_req_rx; - p2p.dev_found = test_dev_found; - p2p.start_listen = test_start_listen; - p2p.stop_listen = test_stop_listen; - p2p.send_probe_resp = test_send_probe_resp; - p2p.sd_request = test_sd_request; - p2p.sd_response = test_sd_response; - p2p.prov_disc_req = test_prov_disc_req; - p2p.prov_disc_resp = test_prov_disc_resp; - - os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN); - - p2p.reg_class = 12; /* TODO: change depending on location */ - /* - * Pick one of the social channels randomly as the listen - * channel. - */ - os_get_random((u8 *) &r, sizeof(r)); - p2p.channel = 1 + (r % 3) * 5; - - /* TODO: change depending on location */ - p2p.op_reg_class = 12; - /* - * For initial tests, pick the operation channel randomly. - * TODO: Use scan results (etc.) to select the best channel. - */ - p2p.op_channel = 1 + r % 11; - - os_memcpy(p2p.country, "US ", 3); - - /* FIX: fetch available channels from the driver */ - p2p.channels.reg_classes = 1; - p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */ - p2p.channels.reg_class[0].channels = 11; - for (i = 0; i < 11; i++) - p2p.channels.reg_class[0].channel[i] = i + 1; - - p2p.max_peers = 100; - - drv->p2p = p2p_init(&p2p); - if (drv->p2p == NULL) - return -1; - return 0; -#else /* CONFIG_P2P */ - wpa_printf(MSG_INFO, "driver_test: P2P support not included"); - return -1; -#endif /* CONFIG_P2P */ -} - - -const struct wpa_driver_ops wpa_driver_test_ops = { - "test", - "wpa_supplicant test driver", - .hapd_init = test_driver_init, - .hapd_deinit = wpa_driver_test_deinit, - .hapd_send_eapol = test_driver_send_eapol, - .send_mlme = wpa_driver_test_send_mlme, - .set_generic_elem = test_driver_set_generic_elem, - .sta_deauth = test_driver_sta_deauth, - .sta_disassoc = test_driver_sta_disassoc, - .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, - .if_add = test_driver_if_add, - .if_remove = test_driver_if_remove, - .hapd_set_ssid = test_driver_set_ssid, - .set_privacy = test_driver_set_privacy, - .set_sta_vlan = test_driver_set_sta_vlan, - .sta_add = test_driver_sta_add, - .send_ether = test_driver_send_ether, - .set_ap_wps_ie = test_driver_set_ap_wps_ie, - .get_bssid = wpa_driver_test_get_bssid, - .get_ssid = wpa_driver_test_get_ssid, - .set_key = wpa_driver_test_set_key, - .deinit = wpa_driver_test_deinit, - .set_param = wpa_driver_test_set_param, - .deauthenticate = wpa_driver_test_deauthenticate, - .associate = wpa_driver_test_associate, - .get_capa = wpa_driver_test_get_capa, - .get_mac_addr = wpa_driver_test_get_mac_addr, - .send_eapol = wpa_driver_test_send_eapol, - .mlme_setprotection = wpa_driver_test_mlme_setprotection, - .get_scan_results2 = wpa_driver_test_get_scan_results2, - .global_init = wpa_driver_test_global_init, - .global_deinit = wpa_driver_test_global_deinit, - .init2 = wpa_driver_test_init2, - .get_interfaces = wpa_driver_test_get_interfaces, - .scan2 = wpa_driver_test_scan, - .set_freq = wpa_driver_test_set_freq, - .send_action = wpa_driver_test_send_action, - .remain_on_channel = wpa_driver_test_remain_on_channel, - .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, - .probe_req_report = wpa_driver_test_probe_req_report, -#ifdef CONFIG_P2P - .p2p_find = wpa_driver_test_p2p_find, - .p2p_stop_find = wpa_driver_test_p2p_stop_find, - .p2p_listen = wpa_driver_test_p2p_listen, - .p2p_connect = wpa_driver_test_p2p_connect, - .wps_success_cb = wpa_driver_test_wps_success_cb, - .p2p_group_formation_failed = - wpa_driver_test_p2p_group_formation_failed, - .p2p_set_params = wpa_driver_test_p2p_set_params, -#endif /* CONFIG_P2P */ -}; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 9733e015638d..a1581b8c9cb1 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -31,10 +31,6 @@ #include "driver.h" #include "driver_wext.h" -#ifdef ANDROID -#include "android_drv.h" -#endif /* ANDROID */ - static int wpa_driver_wext_flush_pmkid(void *priv); static int wpa_driver_wext_get_range(void *priv); static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); @@ -82,7 +78,7 @@ int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { - perror("ioctl[SIOCGIWAP]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno)); ret = -1; } os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); @@ -112,7 +108,7 @@ int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { - perror("ioctl[SIOCSIWAP]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno)); ret = -1; } @@ -138,7 +134,8 @@ int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) iwr.u.essid.length = 32; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", + strerror(errno)); ret = -1; } else { ret = iwr.u.essid.length; @@ -196,7 +193,8 @@ int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) iwr.u.essid.length = ssid_len; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s", + strerror(errno)); ret = -1; } @@ -222,7 +220,8 @@ int wpa_driver_wext_set_freq(void *priv, int freq) iwr.u.freq.e = 1; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", + strerror(errno)); ret = -1; } @@ -299,14 +298,6 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) } wpa_supplicant_event(ctx, EVENT_STKSTART, &data); #endif /* CONFIG_PEERKEY */ -#ifdef ANDROID - } else if (os_strncmp(custom, "STOP", 4) == 0) { - wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); - } else if (os_strncmp(custom, "START", 5) == 0) { - wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); - } else if (os_strncmp(custom, "HANG", 4) == 0) { - wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); -#endif /* ANDROID */ } } @@ -502,11 +493,9 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, "IWEVCUSTOM length"); return; } - buf = os_malloc(iwe->u.data.length + 1); + buf = dup_binstr(custom, iwe->u.data.length); if (buf == NULL) return; - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; wpa_driver_wext_event_wireless_custom(drv->ctx, buf); os_free(buf); break; @@ -829,7 +818,8 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); + wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s", + strerror(errno)); goto err1; } @@ -860,12 +850,6 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) drv->mlme_sock = -1; -#ifdef ANDROID - drv->errors = 0; - drv->driver_is_started = TRUE; - drv->bgscan_enabled = 0; -#endif /* ANDROID */ - if (wpa_driver_wext_finish_drv_init(drv) < 0) goto err3; @@ -1047,7 +1031,8 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) } if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s", + strerror(errno)); ret = -1; } @@ -1102,7 +1087,8 @@ static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, "trying larger buffer (%lu bytes)", (unsigned long) res_buf_len); } else { - perror("ioctl[SIOCGIWSCAN]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s", + strerror(errno)); os_free(res_buf); return NULL; } @@ -1553,7 +1539,8 @@ static int wpa_driver_wext_get_range(void *priv) sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); os_free(range); return -1; } else if (iwr.u.data.length >= minlen && @@ -1588,8 +1575,9 @@ static int wpa_driver_wext_get_range(void *priv) drv->capa.max_scan_ssids = 1; wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " - "flags 0x%x", - drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags); + "flags 0x%llx", + drv->capa.key_mgmt, drv->capa.enc, + (unsigned long long) drv->capa.flags); } else { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " "assuming WPA is not supported"); @@ -1632,7 +1620,8 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); if (ret < 0) - perror("ioctl[SIOCSIWENCODEEXT] PMK"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s", + strerror(errno)); os_free(ext); return ret; @@ -1724,7 +1713,8 @@ static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, ret = -2; } - perror("ioctl[SIOCSIWENCODEEXT]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s", + strerror(errno)); } os_free(ext); @@ -1798,7 +1788,8 @@ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, iwr.u.encoding.length = key_len; if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s", + strerror(errno)); ret = -1; } @@ -1810,7 +1801,9 @@ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, iwr.u.encoding.pointer = (caddr_t) NULL; iwr.u.encoding.length = 0; if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE] (set_tx)"); + wpa_printf(MSG_ERROR, + "ioctl[SIOCSIWENCODE] (set_tx): %s", + strerror(errno)); ret = -1; } } @@ -1859,7 +1852,8 @@ static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, iwr.u.data.length = sizeof(mlme); if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { - perror("ioctl[SIOCSIWMLME]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s", + strerror(errno)); ret = -1; } @@ -1871,10 +1865,8 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) { struct iwreq iwr; const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; -#ifndef ANDROID u8 ssid[32]; int i; -#endif /* ANDROID */ /* * Only force-disconnect when the card is in infrastructure mode, @@ -1884,7 +1876,8 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { - perror("ioctl[SIOCGIWMODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s", + strerror(errno)); iwr.u.mode = IW_MODE_INFRA; } @@ -1895,7 +1888,6 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) "selection on disconnect"); } -#ifndef ANDROID if (drv->cfg80211) { /* * cfg80211 supports SIOCSIWMLME commands, so there is @@ -1921,7 +1913,6 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " "SSID to disconnect"); } -#endif /* ANDROID */ } } @@ -1951,7 +1942,8 @@ static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, iwr.u.data.length = ie_len; if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { - perror("ioctl[SIOCSIWGENIE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s", + strerror(errno)); ret = -1; } @@ -1962,15 +1954,15 @@ static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, int wpa_driver_wext_cipher2wext(int cipher) { switch (cipher) { - case CIPHER_NONE: + case WPA_CIPHER_NONE: return IW_AUTH_CIPHER_NONE; - case CIPHER_WEP40: + case WPA_CIPHER_WEP40: return IW_AUTH_CIPHER_WEP40; - case CIPHER_TKIP: + case WPA_CIPHER_TKIP: return IW_AUTH_CIPHER_TKIP; - case CIPHER_CCMP: + case WPA_CIPHER_CCMP: return IW_AUTH_CIPHER_CCMP; - case CIPHER_WEP104: + case WPA_CIPHER_WEP104: return IW_AUTH_CIPHER_WEP104; default: return 0; @@ -1981,10 +1973,10 @@ int wpa_driver_wext_cipher2wext(int cipher) int wpa_driver_wext_keymgmt2wext(int keymgmt) { switch (keymgmt) { - case KEY_MGMT_802_1X: - case KEY_MGMT_802_1X_NO_WPA: + case WPA_KEY_MGMT_IEEE8021X: + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return IW_AUTH_KEY_MGMT_802_1X; - case KEY_MGMT_PSK: + case WPA_KEY_MGMT_PSK: return IW_AUTH_KEY_MGMT_PSK; default: return 0; @@ -2028,7 +2020,8 @@ wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, } if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s", + strerror(errno)); ret = -1; } @@ -2051,7 +2044,11 @@ int wpa_driver_wext_associate(void *priv, * Stop cfg80211 from trying to associate before we are done * with all parameters. */ - wpa_driver_wext_set_ssid(drv, (u8 *) "", 0); + if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { + wpa_printf(MSG_DEBUG, + "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)"); + /* continue anyway */ + } } if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted) @@ -2080,12 +2077,12 @@ int wpa_driver_wext_associate(void *priv, if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) ret = -1; - if (params->wpa_ie == NULL || params->wpa_ie_len == 0) - value = IW_AUTH_WPA_VERSION_DISABLED; - else if (params->wpa_ie[0] == WLAN_EID_RSN) + if (params->wpa_proto & WPA_PROTO_RSN) value = IW_AUTH_WPA_VERSION_WPA2; - else + else if (params->wpa_proto & WPA_PROTO_WPA) value = IW_AUTH_WPA_VERSION_WPA; + else + value = IW_AUTH_WPA_VERSION_DISABLED; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_VERSION, value) < 0) ret = -1; @@ -2101,10 +2098,10 @@ int wpa_driver_wext_associate(void *priv, if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) ret = -1; - value = params->key_mgmt_suite != KEY_MGMT_NONE || - params->pairwise_suite != CIPHER_NONE || - params->group_suite != CIPHER_NONE || - params->wpa_ie_len; + value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE || + params->pairwise_suite != WPA_CIPHER_NONE || + params->group_suite != WPA_CIPHER_NONE || + (params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA)); if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_PRIVACY_INVOKED, value) < 0) ret = -1; @@ -2112,8 +2109,8 @@ int wpa_driver_wext_associate(void *priv, /* Allow unencrypted EAPOL messages even if pairwise keys are set when * not using WPA. IEEE 802.1X specifies that these frames are not * encrypted, but WPA encrypts them when pairwise keys are in use. */ - if (params->key_mgmt_suite == KEY_MGMT_802_1X || - params->key_mgmt_suite == KEY_MGMT_PSK) + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK) allow_unencrypted_eapol = 0; else allow_unencrypted_eapol = 1; @@ -2139,7 +2136,8 @@ int wpa_driver_wext_associate(void *priv, if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) ret = -1; #endif /* CONFIG_IEEE80211W */ - if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) + if (params->freq.freq && + wpa_driver_wext_set_freq(drv, params->freq.freq) < 0) ret = -1; if (!drv->cfg80211 && wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) @@ -2200,7 +2198,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode) } if (errno != EBUSY) { - perror("ioctl[SIOCSIWMODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s", + strerror(errno)); goto done; } @@ -2209,7 +2208,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode) * down, try to set the mode again, and bring it back up. */ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { - perror("ioctl[SIOCGIWMODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s", + strerror(errno)); goto done; } @@ -2222,7 +2222,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode) /* Try to set the mode again while the interface is down */ iwr.u.mode = new_mode; if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) - perror("ioctl[SIOCSIWMODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s", + strerror(errno)); else ret = 0; @@ -2255,7 +2256,8 @@ static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { if (errno != EOPNOTSUPP) - perror("ioctl[SIOCSIWPMKSA]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s", + strerror(errno)); ret = -1; } @@ -2340,129 +2342,64 @@ static const char * wext_get_radio_name(void *priv) } -#ifdef ANDROID - -static int android_wext_cmd(struct wpa_driver_wext_data *drv, const char *cmd) +static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si) { + struct wpa_driver_wext_data *drv = priv; + struct iw_statistics stats; struct iwreq iwr; - char buf[MAX_DRV_CMD_SIZE]; - int ret; + + os_memset(si, 0, sizeof(*si)); + si->current_signal = -9999; + si->current_noise = 9999; + si->chanwidth = CHAN_WIDTH_UNKNOWN; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) &stats; + iwr.u.data.length = sizeof(stats); + iwr.u.data.flags = 1; - os_memset(buf, 0, sizeof(buf)); - os_strlcpy(buf, cmd, sizeof(buf)); - - iwr.u.data.pointer = buf; - iwr.u.data.length = sizeof(buf); - - ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); - - if (ret < 0) { - wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, - cmd); - drv->errors++; - if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { - drv->errors = 0; - wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE - "HANGED"); - } - return ret; + if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) { + wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s", + strerror(errno)); + return -1; } - drv->errors = 0; + si->current_signal = stats.qual.level - + ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0); + si->current_noise = stats.qual.noise - + ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0); return 0; } -static int wext_sched_scan(void *priv, struct wpa_driver_scan_params *params, - u32 interval) +static int wpa_driver_wext_status(void *priv, char *buf, size_t buflen) { struct wpa_driver_wext_data *drv = priv; - struct iwreq iwr; - int ret = 0, i = 0, bp; - char buf[WEXT_PNO_MAX_COMMAND_SIZE]; - - bp = WEXT_PNOSETUP_HEADER_SIZE; - os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); - buf[bp++] = WEXT_PNO_TLV_PREFIX; - buf[bp++] = WEXT_PNO_TLV_VERSION; - buf[bp++] = WEXT_PNO_TLV_SUBVERSION; - buf[bp++] = WEXT_PNO_TLV_RESERVED; - - while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { - /* - * Check that there is enough space needed for 1 more SSID, the - * other sections and null termination. - */ - if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + - WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) - break; - - wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - buf[bp++] = WEXT_PNO_SSID_SECTION; - buf[bp++] = params->ssids[i].ssid_len; - os_memcpy(&buf[bp], params->ssids[i].ssid, - params->ssids[i].ssid_len); - bp += params->ssids[i].ssid_len; - i++; - } - - buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; - /* TODO: consider using interval parameter (interval in msec) instead - * of hardcoded value here */ - os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", - WEXT_PNO_SCAN_INTERVAL); - bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; - - buf[bp++] = WEXT_PNO_REPEAT_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", - WEXT_PNO_REPEAT); - bp += WEXT_PNO_REPEAT_LENGTH; - - buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", - WEXT_PNO_MAX_REPEAT); - bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.data.pointer = buf; - iwr.u.data.length = bp; - - ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); - if (ret < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", - ret); - drv->errors++; - if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { - drv->errors = 0; - wpa_msg(drv->ctx, MSG_INFO, - WPA_EVENT_DRIVER_STATE "HANGED"); - } - return ret; - } + int res; + char *pos, *end; + unsigned char addr[ETH_ALEN]; - drv->errors = 0; - drv->bgscan_enabled = 1; + pos = buf; + end = buf + buflen; - return android_wext_cmd(drv, "PNOFORCE 1"); -} + if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr)) + return -1; + res = os_snprintf(pos, end - pos, + "ifindex=%d\n" + "ifname=%s\n" + "addr=" MACSTR "\n", + drv->ifindex, + drv->ifname, + MAC2STR(addr)); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; -static int wext_stop_sched_scan(void *priv) -{ - struct wpa_driver_wext_data *drv = priv; - drv->bgscan_enabled = 0; - return android_wext_cmd(drv, "PNOFORCE 0"); + return pos - buf; } -#endif /* ANDROID */ - - const struct wpa_driver_ops wpa_driver_wext_ops = { .name = "wext", .desc = "Linux wireless extensions (generic)", @@ -2482,8 +2419,6 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .get_capa = wpa_driver_wext_get_capa, .set_operstate = wpa_driver_wext_set_operstate, .get_radio_name = wext_get_radio_name, -#ifdef ANDROID - .sched_scan = wext_sched_scan, - .stop_sched_scan = wext_stop_sched_scan, -#endif /* ANDROID */ + .signal_poll = wpa_driver_wext_signal_poll, + .status = wpa_driver_wext_status, }; diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h index c4a5bc99c803..b4b5960a7766 100644 --- a/src/drivers/driver_wext.h +++ b/src/drivers/driver_wext.h @@ -44,12 +44,6 @@ struct wpa_driver_wext_data { int cfg80211; /* whether driver is using cfg80211 */ u8 max_level; - -#ifdef ANDROID - int errors; - int driver_is_started; - int bgscan_enabled; -#endif /* ANDROID */ }; int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index e0f0f2289bce..f95f3ccf5b66 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -17,6 +17,7 @@ #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include +#include #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ #ifdef __sun__ #include @@ -99,7 +100,7 @@ static int wired_multicast_membership(int sock, int ifindex, if (setsockopt(sock, SOL_PACKET, add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt"); + wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); return -1; } return 0; @@ -157,7 +158,7 @@ static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); return; } @@ -175,7 +176,7 @@ static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); return; } @@ -208,19 +209,21 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); if (drv->sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + 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)) { - printf("Could not register read socket\n"); + 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) { - perror("ioctl(SIOCGIFINDEX)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); return -1; } @@ -231,7 +234,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) addr.sll_ifindex); if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } @@ -246,26 +249,28 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFHWADDR)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", + strerror(errno)); return -1; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - printf("Invalid HW-addr family 0x%04x\n", - ifr.ifr_hwaddr.sa_family); + 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) { - perror("socket call failed for dhcp"); + 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)) { - printf("Could not register read socket\n"); + wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } @@ -276,12 +281,14 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + 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) { - perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s", + strerror(errno)); return -1; } @@ -289,13 +296,15 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, (char *) &ifr, sizeof(ifr)) < 0) { - perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + 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) { - perror("bind"); + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } @@ -319,8 +328,9 @@ static int wired_send_eapol(void *priv, const u8 *addr, len = sizeof(*hdr) + data_len; hdr = os_zalloc(len); if (hdr == NULL) { - printf("malloc() failed for wired_send_eapol(len=%lu)\n", - (unsigned long) len); + wpa_printf(MSG_INFO, + "malloc() failed for wired_send_eapol(len=%lu)", + (unsigned long) len); return -1; } @@ -336,9 +346,9 @@ static int wired_send_eapol(void *priv, const u8 *addr, os_free(hdr); if (res < 0) { - perror("wired_send_eapol: send"); - printf("wired_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); + wpa_printf(MSG_ERROR, + "wired_send_eapol - packet len: %lu - failed: send: %s", + (unsigned long) len, strerror(errno)); } return res; @@ -352,7 +362,8 @@ static void * wired_driver_hapd_init(struct hostapd_data *hapd, drv = os_zalloc(sizeof(struct wpa_driver_wired_data)); if (drv == NULL) { - printf("Could not allocate memory for wired driver data\n"); + wpa_printf(MSG_INFO, + "Could not allocate memory for wired driver data"); return NULL; } @@ -373,11 +384,15 @@ static void wired_driver_hapd_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; - if (drv->sock >= 0) + if (drv->sock >= 0) { + eloop_unregister_read_sock(drv->sock); close(drv->sock); + } - if (drv->dhcp_sock >= 0) + if (drv->dhcp_sock >= 0) { + eloop_unregister_read_sock(drv->dhcp_sock); close(drv->dhcp_sock); + } os_free(drv); } @@ -413,14 +428,15 @@ static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + 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) { - perror("ioctl[SIOCGIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); close(s); return -1; } @@ -437,7 +453,7 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } @@ -445,7 +461,8 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_flags = flags & 0xffff; if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCSIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); close(s); return -1; } @@ -454,6 +471,35 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) } +#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; @@ -465,7 +511,7 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } @@ -499,7 +545,8 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOC{ADD/DEL}MULTI]"); + wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", + strerror(errno)); close(s); return -1; } @@ -522,7 +569,7 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) #ifdef __linux__ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); if (drv->pf_sock < 0) - perror("socket(PF_PACKET)"); + wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); #else /* __linux__ */ drv->pf_sock = -1; #endif /* __linux__ */ @@ -562,6 +609,16 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) __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; } diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index a92eddf30295..f0c3bb3c63ba 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -6,8 +6,9 @@ * See README for more details. */ -#include "includes.h" - +#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 */ @@ -18,21 +19,22 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_MADWIFI -extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ -#endif /* CONFIG_DRIVER_MADWIFI */ #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_TEST -extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ -#endif /* CONFIG_DRIVER_TEST */ +#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; @@ -47,30 +49,30 @@ extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ struct wpa_driver_ops *wpa_drivers[] = { -#ifdef CONFIG_DRIVER_WEXT - &wpa_driver_wext_ops, -#endif /* CONFIG_DRIVER_WEXT */ #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_MADWIFI - &wpa_driver_madwifi_ops, -#endif /* CONFIG_DRIVER_MADWIFI */ #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_TEST - &wpa_driver_test_ops, -#endif /* CONFIG_DRIVER_TEST */ +#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 */ diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index c7a98d31dc20..943407875701 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -17,9 +17,18 @@ DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += ../src/drivers/driver_wired.o endif +ifdef CONFIG_DRIVER_MACSEC_QCA +DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA +DRV_OBJS += ../src/drivers/driver_macsec_qca.o +endif + ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += ../src/drivers/driver_nl80211.o +DRV_OBJS += ../src/drivers/driver_nl80211_capa.o +DRV_OBJS += ../src/drivers/driver_nl80211_event.o +DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o +DRV_OBJS += ../src/drivers/driver_nl80211_scan.o DRV_OBJS += ../src/utils/radiotap.o NEED_SME=y NEED_AP_MLME=y @@ -30,7 +39,17 @@ NEED_RFKILL=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 + DRV_CFLAGS += -DCONFIG_LIBNL20 + ifdef LIBNL_INC + DRV_CFLAGS += -I$(LIBNL_INC) + else + PKG_CONFIG ?= pkg-config + DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0) + endif +ifdef CONFIG_LIBNL3_ROUTE + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE +endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -55,10 +74,12 @@ CONFIG_L2_FREEBSD=y CONFIG_DNET_PCAP=y endif -ifdef CONFIG_DRIVER_TEST -DRV_CFLAGS += -DCONFIG_DRIVER_TEST -DRV_OBJS += ../src/drivers/driver_test.o -NEED_AP_MLME=y +ifdef CONFIG_DRIVER_OPENBSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD +DRV_OBJS += ../src/drivers/driver_openbsd.o endif ifdef CONFIG_DRIVER_NONE @@ -77,21 +98,15 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_AP_OBJS += ../src/drivers/driver_madwifi.o -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += ../src/drivers/driver_atheros.o CONFIG_L2_PACKET=linux NEED_NETLINK=y NEED_LINUX_IOCTL=y +ifdef ATH_GCM_SUPPORT +CFLAGS += -DATH_GCM_SUPPORT +endif endif ##### PURE CLIENT DRIVERS diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index 23fcbb7c5b22..8da4c53e0f82 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -20,6 +20,11 @@ endif ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += src/drivers/driver_nl80211.c +DRV_OBJS += src/drivers/driver_nl80211_android.c +DRV_OBJS += src/drivers/driver_nl80211_capa.c +DRV_OBJS += src/drivers/driver_nl80211_event.c +DRV_OBJS += src/drivers/driver_nl80211_monitor.c +DRV_OBJS += src/drivers/driver_nl80211_scan.c DRV_OBJS += src/utils/radiotap.c NEED_SME=y NEED_AP_MLME=y @@ -31,6 +36,10 @@ ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +ifdef CONFIG_LIBNL3_ROUTE + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE +endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -55,10 +64,12 @@ CONFIG_L2_FREEBSD=y CONFIG_DNET_PCAP=y endif -ifdef CONFIG_DRIVER_TEST -DRV_CFLAGS += -DCONFIG_DRIVER_TEST -DRV_OBJS += src/drivers/driver_test.c -NEED_AP_MLME=y +ifdef CONFIG_DRIVER_OPENBSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD +DRV_OBJS += src/drivers/driver_openbsd.c endif ifdef CONFIG_DRIVER_NONE @@ -77,15 +88,6 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_AP_OBJS += src/drivers/driver_madwifi.c -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += src/drivers/driver_atheros.c diff --git a/src/drivers/linux_defines.h b/src/drivers/linux_defines.h new file mode 100644 index 000000000000..a107479a3e10 --- /dev/null +++ b/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 + * + * 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/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index 4380428f979c..837971d25c99 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -204,11 +204,14 @@ int linux_br_del_if(int sock, const char *brname, const char *ifname) int linux_br_get(char *brname, const char *ifname) { char path[128], brlink[128], *pos; + ssize_t res; + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", ifname); - os_memset(brlink, 0, sizeof(brlink)); - if (readlink(path, brlink, sizeof(brlink) - 1) < 0) + res = readlink(path, brlink, sizeof(brlink)); + if (res < 0 || (size_t) res >= sizeof(brlink)) return -1; + brlink[res] = '\0'; pos = os_strrchr(brlink, '/'); if (pos == NULL) return -1; diff --git a/src/drivers/linux_wext.h b/src/drivers/linux_wext.h index 55cf95531808..e7c7001e1cea 100644 --- a/src/drivers/linux_wext.h +++ b/src/drivers/linux_wext.h @@ -19,13 +19,13 @@ #define _LINUX_SOCKET_H #define _LINUX_IF_H -#include +#include #include -typedef __uint32_t __u32; -typedef __int32_t __s32; -typedef __uint16_t __u16; -typedef __int16_t __s16; -typedef __uint8_t __u8; +typedef uint32_t __u32; +typedef int32_t __s32; +typedef uint16_t __u16; +typedef int16_t __s16; +typedef uint8_t __u8; #ifndef __user #define __user #endif /* __user */ diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c index 6c60550fd613..0e960f48c0ad 100644 --- a/src/drivers/netlink.c +++ b/src/drivers/netlink.c @@ -1,6 +1,6 @@ /* * Netlink helper functions for driver wrappers - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -137,6 +137,35 @@ void netlink_deinit(struct netlink_data *netlink) os_free(netlink); } + +static const char * linkmode_str(int mode) +{ + switch (mode) { + case -1: + return "no change"; + case 0: + return "kernel-control"; + case 1: + return "userspace-control"; + } + return "?"; +} + + +static const char * operstate_str(int state) +{ + switch (state) { + case -1: + return "no change"; + case IF_OPER_DORMANT: + return "IF_OPER_DORMANT"; + case IF_OPER_UP: + return "IF_OPER_UP"; + } + return "?"; +} + + int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, int linkmode, int operstate) { @@ -170,8 +199,7 @@ int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, rta->rta_type = IFLA_LINKMODE; rta->rta_len = RTA_LENGTH(sizeof(char)); *((char *) RTA_DATA(rta)) = linkmode; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); + req.hdr.nlmsg_len += RTA_SPACE(sizeof(char)); } if (operstate != -1) { rta = aliasing_hide_typecast( @@ -180,12 +208,12 @@ int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, rta->rta_type = IFLA_OPERSTATE; rta->rta_len = RTA_LENGTH(sizeof(char)); *((char *) RTA_DATA(rta)) = operstate; - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + - RTA_LENGTH(sizeof(char)); + req.hdr.nlmsg_len += RTA_SPACE(sizeof(char)); } - wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); + wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)", + ifindex, linkmode, linkmode_str(linkmode), + operstate, operstate_str(operstate)); ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); if (ret < 0) { diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 1a9a819cfab0..b37bd5a1cb82 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -27,6 +27,8 @@ #include +#define NL80211_GENL_NAME "nl80211" + /** * DOC: Station handling * @@ -36,7 +38,21 @@ * The station is still assumed to belong to the AP interface it was added * to. * - * TODO: need more info? + * Station handling varies per interface type and depending on the driver's + * capabilities. + * + * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS + * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: + * - a setup station entry is added, not yet authorized, without any rate + * or capability information, this just exists to avoid race conditions + * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid + * to add rate and capability information to the station and at the same + * time mark it authorized. + * - %NL80211_TDLS_ENABLE_LINK is then used + * - after this, the only valid operation is to remove it by tearing down + * the TDLS link (%NL80211_TDLS_DISABLE_LINK) + * + * TODO: need more info for other interface types */ /** @@ -109,6 +125,31 @@ * interfaces that a given device supports. */ +/** + * DOC: packet coalesce support + * + * In most cases, host that receives IPv4 and IPv6 multicast/broadcast + * packets does not do anything with these packets. Therefore the + * reception of these unwanted packets causes unnecessary processing + * and power consumption. + * + * Packet coalesce feature helps to reduce number of received interrupts + * to host by buffering these packets in firmware/hardware for some + * predefined time. Received interrupt will be generated when one of the + * following events occur. + * a) Expiration of hardware timer whose expiration time is set to maximum + * coalescing delay of matching coalesce rule. + * b) Coalescing buffer in hardware reaches it's limit. + * c) Packet doesn't match any of the configured coalesce rules. + * + * User needs to configure following parameters for creating a coalesce + * rule. + * a) Maximum coalescing delay + * b) List of packet patterns which needs to be matched + * c) Condition for coalescence. pattern 'match' or 'no match' + * Multiple such rules can be created. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -118,8 +159,9 @@ * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, - * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL @@ -169,9 +211,10 @@ * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, - * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, + * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. * The channel to use can be set on the interface or be given using the - * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP @@ -184,7 +227,11 @@ * the interface identified by %NL80211_ATTR_IFINDEX. * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC * or, if no MAC address given, all stations, on the interface identified - * by %NL80211_ATTR_IFINDEX. + * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and + * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type + * of disconnection indication should be sent to the station + * (Deauthentication or Disassociation frame and reason code for that + * frame). * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by @@ -260,8 +307,9 @@ * passed, all channels allowed for the current regulatory domain * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. - * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT - * if scheduled scan is not running. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if + * scheduled scan is not running. The caller may assume that as soon + * as the call returns, it is safe to start a new scheduled scan again. * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan * results available. * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has @@ -373,10 +421,20 @@ * requests to connect to a specified network but without separating * auth and assoc steps. For this, you need to specify the SSID in a * %NL80211_ATTR_SSID attribute, and can optionally specify the association - * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, - * %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, - * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and - * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, + * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and + * %NL80211_ATTR_WIPHY_FREQ_HINT. + * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are + * restrictions on BSS selection, i.e., they effectively prevent roaming + * within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT + * can be included to provide a recommendation of the initial BSS while + * allowing the driver to roam to other BSSes within the ESS and also to + * ignore this recommendation if the indicated BSS is not ideal. Only one + * set of BSSID,frequency parameters is used (i.e., either the enforcing + * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict + * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT). * Background scan period can optionally be * specified in %NL80211_ATTR_BG_SCAN_PERIOD, * if not specified default background scan configuration @@ -401,8 +459,7 @@ * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the - * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be - * optionally used to specify additional channel parameters. + * frequency for the operation. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the @@ -440,17 +497,19 @@ * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the - * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and - * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. If this - * channel is not the current channel (remain-on-channel or the - * operational channel) the device will switch to the given channel and - * transmit the frame, optionally waiting for a response for the time + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the * management frames at CCK rate or not in 2GHz band. + * %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA + * counters which will be updated to the current value. This attribute + * is used during CSA period. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. @@ -473,8 +532,8 @@ * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ - * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed - * by %NL80211_ATTR_IFINDEX) shall operate on. + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -499,9 +558,11 @@ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a * beacon or probe response from a compatible mesh peer. This is only * sent while no station information (sta_info) exists for the new peer - * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set. On - * reception of this notification, userspace may decide to create a new - * station (@NL80211_CMD_NEW_STATION). To stop this notification from + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH, + * @NL80211_MESH_SETUP_USERSPACE_AMPE, or + * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this + * notification, userspace may decide to create a new station + * (@NL80211_CMD_NEW_STATION). To stop this notification from * reoccurring, the userspace authentication daemon may want to create the * new station with the AUTHENTICATED flag unset and maybe change it later * depending on the authentication result. @@ -513,6 +574,12 @@ * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For * more background information, see * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification + * from the driver reporting the wakeup reason. In this case, the + * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason + * for the wakeup, if it was caused by wireless. If it is not present + * in the wakeup notification, the wireless device didn't cause the + * wakeup but reports that it was woken up. * * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver * the necessary information for supporting GTK rekey offload. This @@ -532,7 +599,14 @@ * operation, %NL80211_ATTR_MAC contains the peer MAC address, and * %NL80211_ATTR_REASON_CODE the reason code to be used (only with * %NL80211_TDLS_TEARDOWN). - * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The + * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be + * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as + * 802.11 management frames, while TDLS action codes (802.11-2012 + * 8.5.13.1) will be encapsulated and sent as data frames. The currently + * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES + * and the currently supported TDLS actions codes are given in + * &enum ieee80211_tdls_actioncode. * * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP * (or GO) interface (i.e. hostapd) to ask for unexpected frames to @@ -568,8 +642,19 @@ * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating - * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. This indication may also be + * sent when a remotely-initiated switch (e.g., when a STA receives a CSA + * from the remote AP) is completed; + * + * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch + * has been started on an interface, regardless of the initiator + * (ie. whether it was requested from a remote device or + * initiated on our own). It indicates that + * %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ + * after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may + * decide to react to this indication by requesting other + * interfaces to change channel as well. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with @@ -587,6 +672,108 @@ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames * for IBSS or MESH vif. * + * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. + * This is to be used with the drivers advertising the support of MAC + * address based access control. List of MAC addresses is passed in + * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in + * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it + * is not already done. The new list will replace any existing list. Driver + * will clear its ACL when the list of MAC addresses passed is empty. This + * command is used in AP/P2P GO mode. Driver has to make sure to clear its + * ACL list during %NL80211_CMD_STOP_AP. + * + * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once + * a radar is detected or the channel availability scan (CAC) has finished + * or was aborted, or a radar was detected, usermode will be notified with + * this event. This command is also used to notify userspace about radars + * while operating on this channel. + * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the + * event. + * + * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features, + * i.e. features for the nl80211 protocol rather than device features. + * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. + * + * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition + * Information Element to the WLAN driver + * + * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver + * to the supplicant. This will carry the target AP's MAC address along + * with the relevant Information Elements. This event is used to report + * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). + * + * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running + * a critical protocol that needs more reliability in the connection to + * complete. + * + * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can + * return back to normal. + * + * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. + * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. + * + * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the + * the new channel information (Channel Switch Announcement - CSA) + * in the beacon for some time (as defined in the + * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the + * new channel. Userspace provides the new channel information (using + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel + * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform + * other station that transmission must be blocked until the channel + * switch is complete. + * + * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified + * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in + * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in + * %NL80211_ATTR_VENDOR_DATA. + * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is + * used in the wiphy data as a nested attribute containing descriptions + * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. + * This may also be sent as an event with the same attributes. + * + * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values. + * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If + * that attribute is not included, QoS mapping is disabled. Since this + * QoS mapping is relevant for IP packets, it is only valid during an + * association. This is cleared on disassociation and AP restart. + * + * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given + * %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO + * and %NL80211_ATTR_ADMITTED_TIME parameters. + * Note that the action frame handshake with the AP shall be handled by + * userspace via the normal management RX/TX framework, this only sets + * up the TX TS in the driver/device. + * If the admitted time attribute is not added then the request just checks + * if a subsequent setup could be successful, the intent is to use this to + * avoid setting up a session with the AP when local restrictions would + * make that impossible. However, the subsequent "real" setup may still + * fail even if the check was successful. + * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID + * and %NL80211_ATTR_MAC parameters. It isn't necessary to call this + * before removing a station entry entirely, or before disassociating + * or similar, cleanup will happen in the driver/device in this case. + * + * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and + * bandwidth of a channel must be given. + * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer, + * identified by the %NL80211_ATTR_MAC parameter. A target channel is + * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining + * channel width/type. The target operating class is given via + * %NL80211_ATTR_OPER_CLASS. + * The driver is responsible for continually initiating channel-switching + * operations and returning to the base channel for communication with the + * AP. + * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS + * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel + * when this command completes. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -737,6 +924,40 @@ enum nl80211_commands { NL80211_CMD_SET_MCAST_RATE, + NL80211_CMD_SET_MAC_ACL, + + NL80211_CMD_RADAR_DETECT, + + NL80211_CMD_GET_PROTOCOL_FEATURES, + + NL80211_CMD_UPDATE_FT_IES, + NL80211_CMD_FT_EVENT, + + NL80211_CMD_CRIT_PROTOCOL_START, + NL80211_CMD_CRIT_PROTOCOL_STOP, + + NL80211_CMD_GET_COALESCE, + NL80211_CMD_SET_COALESCE, + + NL80211_CMD_CHANNEL_SWITCH, + + NL80211_CMD_VENDOR, + + NL80211_CMD_SET_QOS_MAP, + + NL80211_CMD_ADD_TX_TS, + NL80211_CMD_DEL_TX_TS, + + NL80211_CMD_GET_MPP, + + NL80211_CMD_JOIN_OCB, + NL80211_CMD_LEAVE_OCB, + + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, + + NL80211_CMD_TDLS_CHANNEL_SWITCH, + NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -773,14 +994,26 @@ enum nl80211_commands { * /sys/class/ieee80211//index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters - * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ - * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * if HT20 or HT40 are to be used (i.e., HT disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * This attribute is now deprecated. * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 @@ -844,7 +1077,8 @@ enum nl80211_commands { * consisting of a nested array. * * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). - * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link + * (see &enum nl80211_plink_action). * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path * info given for %NL80211_CMD_GET_MPATH, nested attribute described at @@ -859,7 +1093,7 @@ enum nl80211_commands { * to query the CRDA to retrieve one regulatory domain. This attribute can * also be used by userspace to query the kernel for the currently set * regulatory domain. We chose an alpha2 as that is also used by the - * IEEE-802.11d country information element to identify a country. + * IEEE-802.11 country information element to identify a country. * Users can also simply ask the wireless core to set regulatory domain * to a specific alpha2. * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory @@ -947,7 +1181,7 @@ enum nl80211_commands { * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); * this attribute can be used - * with %NL80211_CMD_ASSOCIATE request + * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. @@ -1127,10 +1361,10 @@ enum nl80211_commands { * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver * allows auth frames in a mesh to be passed to userspace for processing via * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. - * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as - * defined in &enum nl80211_plink_state. Used when userspace is - * driving the peer link management state machine. - * @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in + * &enum nl80211_plink_state. Used when userspace is driving the peer link + * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or + * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled. * * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy * capabilities, the supported WoWLAN triggers @@ -1292,6 +1526,169 @@ enum nl80211_commands { * * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) * + * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with + * the START_AP and SET_BSS commands + * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the + * START_AP and SET_BSS commands. This can have the values 0 or 1; + * if not given in START_AP 0 is assumed, if not given in SET_BSS + * no change is made. + * + * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode + * defined in &enum nl80211_mesh_power_mode. + * + * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, + * carried in a u32 attribute + * + * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for + * MAC ACL. + * + * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum + * number of MAC addresses that a device can support for MAC + * ACL. + * + * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, + * contains a value of enum nl80211_radar_event (u32). + * + * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver + * has and handles. The format is the same as the IE contents. See + * 802.11-2012 8.4.2.29 for more information. + * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver + * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. + * + * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to + * the driver, e.g., to enable TDLS power save (PU-APSD). + * + * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are + * advertised to the driver, e.g., to enable TDLS off channel operations + * and PU-APSD. + * + * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see + * &enum nl80211_protocol_features, the attribute is a u32. + * + * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports + * receiving the data for a single wiphy split across multiple + * messages, given with wiphy dump message + * + * @NL80211_ATTR_MDID: Mobility Domain Identifier + * + * @NL80211_ATTR_IE_RIC: Resource Information Container Information + * Element + * + * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased + * reliability, see &enum nl80211_crit_proto_id (u16). + * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which + * the connection should have increased reliability (u16). + * + * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). + * This is similar to @NL80211_ATTR_STA_AID but with a difference of being + * allowed to be used with the first @NL80211_CMD_SET_STATION command to + * update a TDLS peer STA entry. + * + * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. + * + * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's + * until the channel switch event. + * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission + * must be blocked on the current channel (before the channel switch + * operation). + * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information + * for the time while performing a channel switch. + * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel + * switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel + * switch counters in the probe response (%NL80211_ATTR_PROBE_RESP). + * + * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. + * As specified in the &enum nl80211_rxmgmt_flags. + * + * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. + * + * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported + * supported operating classes. + * + * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space + * controls DFS operation in IBSS mode. If the flag is included in + * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS + * channels and reports radar events to userspace. Userspace is required + * to react to radar events, e.g. initiate a channel switch or leave the + * IBSS network. + * + * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports + * 5 MHz channel bandwidth. + * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports + * 10 MHz channel bandwidth. + * + * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode + * Notification Element based on association request when used with + * %NL80211_CMD_NEW_STATION; u8 attribute. + * + * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if + * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) + * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command + * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this + * attribute is also used for vendor command feature advertisement + * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy + * info, containing a nested array of possible events + * + * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This + * data is in the format defined for the payload of the QoS Map Set element + * in IEEE Std 802.11-2012, 8.4.2.97. + * + * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS + * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS + * + * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many + * associated stations are supported in AP mode (including P2P GO); u32. + * Since drivers may not have a fixed limit on the maximum number (e.g., + * other concurrent operations may affect this), drivers are allowed to + * advertise values that cannot always be met. In such cases, an attempt + * to add a new station entry with @NL80211_CMD_NEW_STATION may fail. + * + * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which + * should be updated when the frame is transmitted. + * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum + * supported number of csa counters. + * + * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. + * As specified in the &enum nl80211_tdls_peer_capability. + * + * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface + * creation then the new interface will be owned by the netlink socket + * that created it and will be destroyed when the socket is closed. + * + * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is + * the TDLS link initiator. + * + * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection + * shall support Radio Resource Measurements (11k). This attribute can be + * used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests. + * User space applications are expected to use this flag only if the + * underlying device supports these minimal RRM features: + * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES, + * %NL80211_FEATURE_QUIET, + * If this flag is used, driver must add the Power Capabilities IE to the + * association request. In addition, it must also set the RRM capability + * flag in the association request's Capability Info field. + * + * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout + * estimation algorithm (dynack). In order to activate dynack + * %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower + * drivers to indicate dynack capability. Dynack is automatically disabled + * setting valid value for coverage class. + * + * @NL80211_ATTR_TSID: a TSID value (u8 attribute) + * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute) + * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds + * (per second) (u16 attribute) + * + * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see + * &enum nl80211_smps_mode. + * + * @NL80211_ATTR_OPER_CLASS: operating class + * + * @NL80211_ATTR_MAC_MASK: MAC address mask + * + * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1555,15 +1952,110 @@ enum nl80211_attrs { NL80211_ATTR_SCAN_FLAGS, + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + + NL80211_ATTR_P2P_CTWINDOW, + NL80211_ATTR_P2P_OPPPS, + + NL80211_ATTR_LOCAL_MESH_POWER_MODE, + + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_MAC_ACL_MAX, + + NL80211_ATTR_RADAR_EVENT, + + NL80211_ATTR_EXT_CAPA, + NL80211_ATTR_EXT_CAPA_MASK, + + NL80211_ATTR_STA_CAPABILITY, + NL80211_ATTR_STA_EXT_CAPABILITY, + + NL80211_ATTR_PROTOCOL_FEATURES, + NL80211_ATTR_SPLIT_WIPHY_DUMP, + + NL80211_ATTR_DISABLE_VHT, + NL80211_ATTR_VHT_CAPABILITY_MASK, + + NL80211_ATTR_MDID, + NL80211_ATTR_IE_RIC, + + NL80211_ATTR_CRIT_PROT_ID, + NL80211_ATTR_MAX_CRIT_PROT_DURATION, + + NL80211_ATTR_PEER_AID, + + NL80211_ATTR_COALESCE_RULE, + + NL80211_ATTR_CH_SWITCH_COUNT, + NL80211_ATTR_CH_SWITCH_BLOCK_TX, + NL80211_ATTR_CSA_IES, + NL80211_ATTR_CSA_C_OFF_BEACON, + NL80211_ATTR_CSA_C_OFF_PRESP, + + NL80211_ATTR_RXMGMT_FLAGS, + + NL80211_ATTR_STA_SUPPORTED_CHANNELS, + + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + + NL80211_ATTR_HANDLE_DFS, + + NL80211_ATTR_SUPPORT_5_MHZ, + NL80211_ATTR_SUPPORT_10_MHZ, + + NL80211_ATTR_OPMODE_NOTIF, + + NL80211_ATTR_VENDOR_ID, + NL80211_ATTR_VENDOR_SUBCMD, + NL80211_ATTR_VENDOR_DATA, + NL80211_ATTR_VENDOR_EVENTS, + + NL80211_ATTR_QOS_MAP, + + NL80211_ATTR_MAC_HINT, + NL80211_ATTR_WIPHY_FREQ_HINT, + + NL80211_ATTR_MAX_AP_ASSOC_STA, + + NL80211_ATTR_TDLS_PEER_CAPABILITY, + + NL80211_ATTR_SOCKET_OWNER, + + NL80211_ATTR_CSA_C_OFFSETS_TX, + NL80211_ATTR_MAX_CSA_COUNTERS, + + NL80211_ATTR_TDLS_INITIATOR, + + NL80211_ATTR_USE_RRM, + + NL80211_ATTR_WIPHY_DYN_ACK, + + NL80211_ATTR_TSID, + NL80211_ATTR_USER_PRIO, + NL80211_ATTR_ADMITTED_TIME, + + NL80211_ATTR_SMPS_MODE, + + NL80211_ATTR_OPER_CLASS, + + NL80211_ATTR_MAC_MASK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, + NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; /* source-level API compatibility */ #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG +#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -1629,6 +2121,8 @@ enum nl80211_attrs { * and therefore can't be created in the normal ways, use the * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE * commands to create and destroy one + * @NL80211_IF_TYPE_OCB: Outside Context of a BSS + * This mode corresponds to the MIB variable dot11OCBActivated=true * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -1648,6 +2142,7 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, NL80211_IFTYPE_P2P_DEVICE, + NL80211_IFTYPE_OCB, /* keep last */ NUM_NL80211_IFTYPES, @@ -1672,6 +2167,9 @@ enum nl80211_iftype { * flag can't be changed, it is only valid while adding a station, and * attempts to change it will silently be ignored (rather than rejected * as errors.) + * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers + * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a + * previously added station into associated state * @NL80211_STA_FLAG_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */ @@ -1683,6 +2181,7 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_MFP, NL80211_STA_FLAG_AUTHENTICATED, NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_ASSOCIATED, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, @@ -1719,10 +2218,15 @@ struct nl80211_sta_flag_update { * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) - * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) + * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) + * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate + * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -1732,6 +2236,11 @@ enum nl80211_rate_info { NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, NL80211_RATE_INFO_BITRATE32, + NL80211_RATE_INFO_VHT_MCS, + NL80211_RATE_INFO_VHT_NSS, + NL80211_RATE_INFO_80_MHZ_WIDTH, + NL80211_RATE_INFO_80P80_MHZ_WIDTH, + NL80211_RATE_INFO_160_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -1778,6 +2287,8 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_rate_info @@ -1799,6 +2310,16 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) + * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU + * Contains a nested array of signal strength attributes (u8, dBm) + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. + * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the + * 802.11 header (u32, kbps) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1823,6 +2344,14 @@ enum nl80211_sta_info { NL80211_STA_INFO_STA_FLAGS, NL80211_STA_INFO_BEACON_LOSS, NL80211_STA_INFO_T_OFFSET, + NL80211_STA_INFO_LOCAL_PM, + NL80211_STA_INFO_PEER_PM, + NL80211_STA_INFO_NONPEER_PM, + NL80211_STA_INFO_RX_BYTES64, + NL80211_STA_INFO_TX_BYTES64, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + NL80211_STA_INFO_EXPECTED_THROUGHPUT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -1924,26 +2453,76 @@ enum nl80211_band_attr { * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current * regulatory domain. - * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is - * permitted on this channel in current regulatory domain. - * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted - * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation + * are permitted on this channel, this includes sending probe + * requests, or modes of operation that require beaconing. * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm * (100 * dBm). + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS + * (enum nl80211_dfs_state) + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * this channel is in this DFS state. + * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel + * as the primary or any of the secondary channels isn't possible, + * this includes 80+80 channels + * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel + * using this channel as the primary or any of the secondary channels + * isn't possible + * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this + * channel. A channel that has the INDOOR_ONLY attribute can only be + * used when there is a clear assessment that the device is operating in + * an indoor surroundings, i.e., it is connected to AC power (and not + * through portable DC inverters) or is under the control of a master + * that is acting as an AP and is connected to AC power. + * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this + * channel if it's connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a + * channel that has the GO_CONCURRENT attribute set can be done when there + * is a clear assessment that the device is operating under the guidance of + * an authorized master, i.e., setting up a GO while the device is also + * connected to an AP with DFS and radar detection on the UNII band (it is + * up to user-space, i.e., wpa_supplicant to perform the required + * verifications) + * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed + * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + * + * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122 + * for more information on the FCC description of the relaxations allowed + * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and + * NL80211_FREQUENCY_ATTR_GO_CONCURRENT. */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, NL80211_FREQUENCY_ATTR_FREQ, NL80211_FREQUENCY_ATTR_DISABLED, - NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, - NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_NO_IR, + __NL80211_FREQUENCY_ATTR_NO_IBSS, NL80211_FREQUENCY_ATTR_RADAR, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + NL80211_FREQUENCY_ATTR_DFS_STATE, + NL80211_FREQUENCY_ATTR_DFS_TIME, + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, + NL80211_FREQUENCY_ATTR_NO_80MHZ, + NL80211_FREQUENCY_ATTR_NO_160MHZ, + NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, + NL80211_FREQUENCY_ATTR_INDOOR_ONLY, + NL80211_FREQUENCY_ATTR_GO_CONCURRENT, + NL80211_FREQUENCY_ATTR_NO_20MHZ, + NL80211_FREQUENCY_ATTR_NO_10MHZ, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -1951,6 +2530,9 @@ enum nl80211_frequency_attr { }; #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER +#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR /** * enum nl80211_bitrate_attr - bitrate attributes @@ -2031,12 +2613,14 @@ enum nl80211_reg_type { * in KHz. This is not a center a frequency but an actual regulatory * band edge. * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this - * frequency range, in KHz. + * frequency range, in KHz. * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain * for a given frequency range. The value is in mBi (100 * dBi). * If you don't have one then don't send this. * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for * a given frequency range. The value is in mBm (100 * dBm). + * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds. + * If not present or 0 default CAC time will be used. * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number * currently defined * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use @@ -2052,6 +2636,8 @@ enum nl80211_reg_rule_attr { NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, NL80211_ATTR_POWER_RULE_MAX_EIRP, + NL80211_ATTR_DFS_CAC_TIME, + /* keep last */ __NL80211_REG_RULE_ATTR_AFTER_LAST, NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 @@ -2061,9 +2647,15 @@ enum nl80211_reg_rule_attr { * enum nl80211_sched_scan_match_attr - scheduled scan match attributes * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, - * only report BSS with matching SSID. + * only report BSS with matching SSID. * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a - * BSS in scan results. Filtering is turned off if not specified. + * BSS in scan results. Filtering is turned off if not specified. Note that + * if this attribute is in a match set of its own, then it is treated as + * the default value for all matchsets with an SSID, rather than being a + * matchset of its own without an RSSI filter. This is due to problems with + * how this API was implemented in the past. Also, due to the same problem, + * the only way to create a matchset with only an RSSI filter (with this + * attribute) is if there's only a single matchset with the RSSI attribute. * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -2093,8 +2685,17 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_DFS: DFS support is required to be used * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links - * @NL80211_RRF_PASSIVE_SCAN: passive scan is required - * @NL80211_RRF_NO_IBSS: no IBSS is allowed + * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed, + * this includes probe requests or modes of operation that require + * beaconing. + * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated + * base on contiguous rules and wider channels will be allowed to cross + * multiple contiguous/overlapping frequency ranges. + * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT + * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation + * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation + * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed + * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -2104,10 +2705,25 @@ enum nl80211_reg_rule_flags { NL80211_RRF_DFS = 1<<4, NL80211_RRF_PTP_ONLY = 1<<5, NL80211_RRF_PTMP_ONLY = 1<<6, - NL80211_RRF_PASSIVE_SCAN = 1<<7, - NL80211_RRF_NO_IBSS = 1<<8, + NL80211_RRF_NO_IR = 1<<7, + __NL80211_RRF_NO_IBSS = 1<<8, + NL80211_RRF_AUTO_BW = 1<<11, + NL80211_RRF_GO_CONCURRENT = 1<<12, + NL80211_RRF_NO_HT40MINUS = 1<<13, + NL80211_RRF_NO_HT40PLUS = 1<<14, + NL80211_RRF_NO_80MHZ = 1<<15, + NL80211_RRF_NO_160MHZ = 1<<16, }; +#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR +#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ + NL80211_RRF_NO_HT40PLUS) + +/* For backport compatibility with older userspace */ +#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) + /** * enum nl80211_dfs_regions - regulatory DFS regions * @@ -2137,10 +2753,13 @@ enum nl80211_dfs_regions { * present has been registered with the wireless core that * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a * supported feature. + * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the + * platform is operating in an indoor environment. */ enum nl80211_user_reg_hint_type { NL80211_USER_REG_HINT_USER = 0, NL80211_USER_REG_HINT_CELL_BASE = 1, + NL80211_USER_REG_HINT_INDOOR = 2, }; /** @@ -2196,6 +2815,8 @@ enum nl80211_survey_info { * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. * overrides all other flags. + * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address + * and ACK incoming unicast packets. * * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag @@ -2207,12 +2828,41 @@ enum nl80211_mntr_flags { NL80211_MNTR_FLAG_CONTROL, NL80211_MNTR_FLAG_OTHER_BSS, NL80211_MNTR_FLAG_COOK_FRAMES, + NL80211_MNTR_FLAG_ACTIVE, /* keep last */ __NL80211_MNTR_FLAG_AFTER_LAST, NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 }; +/** + * enum nl80211_mesh_power_mode - mesh power save modes + * + * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is + * not known or has not been set yet. + * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is + * in Awake state all the time. + * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will + * alternate between Active and Doze states, but will wake up for + * neighbor's beacons. + * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will + * alternate between Active and Doze states, but may not wake up + * for neighbor's beacons. + * + * @__NL80211_MESH_POWER_AFTER_LAST - internal use + * @NL80211_MESH_POWER_MAX - highest possible power save level + */ + +enum nl80211_mesh_power_mode { + NL80211_MESH_POWER_UNKNOWN, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_LIGHT_SLEEP, + NL80211_MESH_POWER_DEEP_SLEEP, + + __NL80211_MESH_POWER_AFTER_LAST, + NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +}; + /** * enum nl80211_meshconf_params - mesh configuration parameters * @@ -2240,8 +2890,10 @@ enum nl80211_mntr_flags { * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh * point. * - * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically - * open peer links when we detect compatible mesh peers. + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open + * peer links when we detect compatible mesh peers. Disabled if + * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are + * set. * * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames * containing a PREQ that an MP can send to a particular destination (path @@ -2307,6 +2959,15 @@ enum nl80211_mntr_flags { * (in TUs) during which a mesh STA can send only one Action frame * containing a PREQ element for root path confirmation. * + * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. + * type &enum nl80211_mesh_power_mode (u32) + * + * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) + * + * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've + * established peering with for longer than this time (in seconds), then + * remove it from the STA's list of peers. Default is 30 minutes. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2336,6 +2997,9 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + NL80211_MESHCONF_POWER_MODE, + NL80211_MESHCONF_AWAKE_WINDOW, + NL80211_MESHCONF_PLINK_TIMEOUT, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -2380,6 +3044,13 @@ enum nl80211_meshconf_params { * vendor specific synchronization method or disable it to use the default * neighbor offset synchronization * + * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will + * implement an MPM which handles peer allocation and state. + * + * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication + * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). + * Default is no authentication method required. + * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use @@ -2392,6 +3063,8 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_USERSPACE_AUTH, NL80211_MESH_SETUP_USERSPACE_AMPE, NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, + NL80211_MESH_SETUP_USERSPACE_MPM, + NL80211_MESH_SETUP_AUTH_PROTOCOL, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, @@ -2440,6 +3113,15 @@ enum nl80211_ac { #define NL80211_TXQ_Q_BE NL80211_AC_BE #define NL80211_TXQ_Q_BK NL80211_AC_BK +/** + * enum nl80211_channel_type - channel type + * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel + * @NL80211_CHAN_HT20: 20 MHz HT channel + * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel + * below the control channel + * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel + * above the control channel + */ enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, @@ -2447,6 +3129,51 @@ enum nl80211_channel_type { NL80211_CHAN_HT40PLUS }; +/** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel + * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, + NL80211_CHAN_WIDTH_5, + NL80211_CHAN_WIDTH_10, +}; + +/** + * enum nl80211_bss_scan_width - control channel width for a BSS + * + * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute. + * + * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible + * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide + * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide + */ +enum nl80211_bss_scan_width { + NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_10, + NL80211_BSS_CHAN_WIDTH_5, +}; + /** * enum nl80211_bss - netlink attributes for a BSS * @@ -2454,14 +3181,20 @@ enum nl80211_channel_type { * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * (if @NL80211_BSS_PRESP_DATA is present then this is known to be + * from a probe response, otherwise it may be from the same beacon + * that the NL80211_BSS_BEACON_TSF will be from) * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the * raw information elements from the probe response/beacon (bin); - * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are - * from a Probe Response frame; otherwise they are from a Beacon frame. + * if the %NL80211_BSS_BEACON_IES attribute is present and the data is + * different then the IEs here are from a Probe Response frame; otherwise + * they are from a Beacon frame. * However, if the driver does not indicate the source of the IEs, these * IEs may be from either frame subtype. + * If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the + * data here is known to be from a probe response, without any heuristics. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon @@ -2471,6 +3204,12 @@ enum nl80211_channel_type { * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information * elements from a Beacon frame (bin); not present if no Beacon frame has * yet been received + * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel + * (u32, enum nl80211_bss_scan_width) + * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64) + * (not present if no beacon frame has been received yet) + * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and + * @NL80211_BSS_TSF is known to be from a probe response (flag attribute) * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -2487,6 +3226,9 @@ enum nl80211_bss { NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, + NL80211_BSS_CHAN_WIDTH, + NL80211_BSS_BEACON_TSF, + NL80211_BSS_PRESP_DATA, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -2627,21 +3369,43 @@ enum nl80211_key_attributes { * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). - * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. + * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_vht + * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, - NL80211_TXRATE_MCS, + NL80211_TXRATE_HT, + NL80211_TXRATE_VHT, + NL80211_TXRATE_GI, /* keep last */ __NL80211_TXRATE_AFTER_LAST, NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 }; +#define NL80211_TXRATE_MCS NL80211_TXRATE_HT +#define NL80211_VHT_NSS_MAX 8 + +/** + * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_vht { + __u16 mcs[NL80211_VHT_NSS_MAX]; +}; + +enum nl80211_txrate_gi { + NL80211_TXRATE_DEFAULT_GI, + NL80211_TXRATE_FORCE_SGI, + NL80211_TXRATE_FORCE_LGI, +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band @@ -2687,6 +3451,8 @@ enum nl80211_ps_state { * interval in which %NL80211_ATTR_CQM_TXE_PKTS and * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. + * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon + * loss event * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -2699,6 +3465,7 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_TXE_RATE, NL80211_ATTR_CQM_TXE_PKTS, NL80211_ATTR_CQM_TXE_INTVL, + NL80211_ATTR_CQM_BEACON_LOSS_EVENT, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -2711,9 +3478,7 @@ enum nl80211_attr_cqm { * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold - * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. - * (Note that deauth/disassoc will still follow if the AP is not - * available. This event might get used as roaming event, etc.) + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, @@ -2735,49 +3500,65 @@ enum nl80211_tx_power_setting { }; /** - * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute - * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute - * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * enum nl80211_packet_pattern_attr - packet pattern attribute + * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has * a zero bit are ignored - * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have * a bit for each byte in the pattern. The lowest-order bit corresponds * to the first byte of the pattern, but the bytes of the pattern are * in a little-endian-like format, i.e. the 9th byte of the pattern * corresponds to the lowest-order bit in the second byte of the mask. * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where * xx indicates "don't care") would be represented by a pattern of - * twelve zero bytes, and a mask of "0xed,0x07". + * twelve zero bytes, and a mask of "0xed,0x01". * Note that the pattern matching is done as though frames were not * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * first (including SNAP header unpacking) and then matched. - * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes - * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after + * these fixed number of bytes of received packet + * @NUM_NL80211_PKTPAT: number of attributes + * @MAX_NL80211_PKTPAT: max attribute number */ -enum nl80211_wowlan_packet_pattern_attr { - __NL80211_WOWLAN_PKTPAT_INVALID, - NL80211_WOWLAN_PKTPAT_MASK, - NL80211_WOWLAN_PKTPAT_PATTERN, - - NUM_NL80211_WOWLAN_PKTPAT, - MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, +enum nl80211_packet_pattern_attr { + __NL80211_PKTPAT_INVALID, + NL80211_PKTPAT_MASK, + NL80211_PKTPAT_PATTERN, + NL80211_PKTPAT_OFFSET, + + NUM_NL80211_PKTPAT, + MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1, }; /** - * struct nl80211_wowlan_pattern_support - pattern support information + * struct nl80211_pattern_support - packet pattern support information * @max_patterns: maximum number of patterns supported * @min_pattern_len: minimum length of each pattern * @max_pattern_len: maximum length of each pattern + * @max_pkt_offset: maximum Rx packet offset * * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when - * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the - * capability information given by the kernel to userspace. + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in + * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of + * %NL80211_ATTR_COALESCE_RULE in the capability information given + * by the kernel to userspace. */ -struct nl80211_wowlan_pattern_support { +struct nl80211_pattern_support { __u32 max_patterns; __u32 min_pattern_len; __u32 max_pattern_len; + __u32 max_pkt_offset; } __attribute__((packed)); +/* only for backward compatibility */ +#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID +#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK +#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN +#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET +#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT +#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT +#define nl80211_wowlan_pattern_support nl80211_pattern_support + /** * enum nl80211_wowlan_triggers - WoWLAN trigger definitions * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes @@ -2791,12 +3572,17 @@ struct nl80211_wowlan_pattern_support { * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns * which are passed in an array of nested attributes, each nested attribute * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. - * Each pattern defines a wakeup packet. The matching is done on the MSDU, - * i.e. as though the packet was an 802.3 packet, so the pattern matching - * is done after the packet is converted to the MSDU. + * Each pattern defines a wakeup packet. Packet offset is associated with + * each pattern which is used while matching the pattern. The matching is + * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the + * pattern matching is done after the packet is converted to the MSDU. * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute - * carrying a &struct nl80211_wowlan_pattern_support. + * carrying a &struct nl80211_pattern_support. + * + * When reporting wakeup. it is a u32 attribute containing the 0-based + * index of the pattern that caused the wakeup, in the patterns passed + * to the kernel when configuring. * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be * used when setting, used only to indicate that GTK rekeying is supported * by the device (flag) @@ -2807,8 +3593,55 @@ struct nl80211_wowlan_pattern_support { * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released * (on devices that have rfkill in the device) (flag) + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains + * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame + * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN + * attribute contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the + * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may + * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute + * contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section + * "TCP connection wakeup" for more details. This is a nested attribute + * containing the exact information for establishing and keeping alive + * the TCP connection. + * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the + * wakeup packet was received on the TCP connection + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the + * TCP connection was lost or failed to be established + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, + * the TCP connection ran out of tokens to use for data to send to the + * service + * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network + * is detected. This is a nested attribute that contains the + * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It + * specifies how the scan is performed (e.g. the interval and the + * channels to scan) as well as the scan results that will + * trigger a wake (i.e. the matchsets). + * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute + * containing an array with information about what triggered the + * wake up. If no elements are present in the array, it means + * that the information is not available. If more than one + * element is present, it means that more than one match + * occurred. + * Each element in the array is a nested attribute that contains + * one optional %NL80211_ATTR_SSID attribute and one optional + * %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of + * these attributes must be present. If + * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one + * frequency, it means that the match occurred in more than one + * channel. * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + * + * These nested attributes are used to configure the wakeup triggers and + * to report the wakeup reason(s). */ enum nl80211_wowlan_triggers { __NL80211_WOWLAN_TRIG_INVALID, @@ -2821,12 +3654,181 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, + NL80211_WOWLAN_TRIG_TCP_CONNECTION, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, + NL80211_WOWLAN_TRIG_NET_DETECT, + NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, /* keep last */ NUM_NL80211_WOWLAN_TRIG, MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 }; +/** + * DOC: TCP connection wakeup + * + * Some devices can establish a TCP connection in order to be woken up by a + * packet coming in from outside their network segment, or behind NAT. If + * configured, the device will establish a TCP connection to the given + * service, and periodically send data to that service. The first data + * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK. + * The data packets can optionally include a (little endian) sequence + * number (in the TCP payload!) that is generated by the device, and, also + * optionally, a token from a list of tokens. This serves as a keep-alive + * with the service, and for NATed connections, etc. + * + * During this keep-alive period, the server doesn't send any data to the + * client. When receiving data, it is compared against the wakeup pattern + * (and mask) and if it matches, the host is woken up. Similarly, if the + * connection breaks or cannot be established to start with, the host is + * also woken up. + * + * Developer's note: ARP offload is required for this, otherwise TCP + * response packets might not go through correctly. + */ + +/** + * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence + * @start: starting value + * @offset: offset of sequence number in packet + * @len: length of the sequence value to write, 1 through 4 + * + * Note: don't confuse with the TCP sequence number(s), this is for the + * keepalive packet payload. The actual value is written into the packet + * in little endian. + */ +struct nl80211_wowlan_tcp_data_seq { + __u32 start, offset, len; +}; + +/** + * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config + * @offset: offset of token in packet + * @len: length of each token + * @token_stream: stream of data to be used for the tokens, the length must + * be a multiple of @len for this to make sense + */ +struct nl80211_wowlan_tcp_data_token { + __u32 offset, len; + __u8 token_stream[]; +}; + +/** + * struct nl80211_wowlan_tcp_data_token_feature - data token features + * @min_len: minimum token length + * @max_len: maximum token length + * @bufsize: total available token buffer size (max size of @token_stream) + */ +struct nl80211_wowlan_tcp_data_token_feature { + __u32 min_len, max_len, bufsize; +}; + +/** + * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters + * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order) + * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address + * (in network byte order) + * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because + * route lookup when configured might be invalid by the time we suspend, + * and doing a route lookup when suspending is no longer possible as it + * might require ARP querying. + * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a + * socket and port will be allocated + * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16) + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte. + * For feature advertising, a u32 attribute holding the maximum length + * of the data payload. + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration + * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature + * advertising it is just a flag + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration, + * see &struct nl80211_wowlan_tcp_data_token and for advertising see + * &struct nl80211_wowlan_tcp_data_token_feature. + * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum + * interval in feature advertising (u32) + * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a + * u32 attribute holding the maximum length + * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for + * feature advertising. The mask works like @NL80211_PKTPAT_MASK + * but on the TCP payload only. + * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes + * @MAX_NL80211_WOWLAN_TCP: highest attribute number + */ +enum nl80211_wowlan_tcp_attrs { + __NL80211_WOWLAN_TCP_INVALID, + NL80211_WOWLAN_TCP_SRC_IPV4, + NL80211_WOWLAN_TCP_DST_IPV4, + NL80211_WOWLAN_TCP_DST_MAC, + NL80211_WOWLAN_TCP_SRC_PORT, + NL80211_WOWLAN_TCP_DST_PORT, + NL80211_WOWLAN_TCP_DATA_PAYLOAD, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + NL80211_WOWLAN_TCP_DATA_INTERVAL, + NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + NL80211_WOWLAN_TCP_WAKE_MASK, + + /* keep last */ + NUM_NL80211_WOWLAN_TCP, + MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 +}; + +/** + * struct nl80211_coalesce_rule_support - coalesce rule support information + * @max_rules: maximum number of rules supported + * @pat: packet pattern support information + * @max_delay: maximum supported coalescing delay in msecs + * + * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the + * capability information given by the kernel to userspace. + */ +struct nl80211_coalesce_rule_support { + __u32 max_rules; + struct nl80211_pattern_support pat; + __u32 max_delay; +} __attribute__((packed)); + +/** + * enum nl80211_attr_coalesce_rule - coalesce rule attribute + * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute + * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing + * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, + * see &enum nl80211_coalesce_condition. + * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched + * after these fixed number of bytes of received packet + * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes + * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number + */ +enum nl80211_attr_coalesce_rule { + __NL80211_COALESCE_RULE_INVALID, + NL80211_ATTR_COALESCE_RULE_DELAY, + NL80211_ATTR_COALESCE_RULE_CONDITION, + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, + + /* keep last */ + NUM_NL80211_ATTR_COALESCE_RULE, + NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 +}; + +/** + * enum nl80211_coalesce_condition - coalesce rule conditions + * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * in a rule are matched. + * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns + * in a rule are not matched. + */ +enum nl80211_coalesce_condition { + NL80211_COALESCE_CONDITION_MATCH, + NL80211_COALESCE_CONDITION_NO_MATCH +}; + /** * enum nl80211_iface_limit_attrs - limit attributes * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) @@ -2863,6 +3865,10 @@ enum nl80211_iface_limit_attrs { * the infrastructure network's beacon interval. * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many * different channels may be used within this group. + * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap + * of supported channel widths for radar detection. + * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap + * of supported regulatory regions for radar detection. * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -2895,6 +3901,8 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_MAXNUM, NL80211_IFACE_COMB_STA_AP_BI_MATCH, NL80211_IFACE_COMB_NUM_CHANNELS, + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, /* keep last */ NUM_NL80211_IFACE_COMB, @@ -2934,6 +3942,23 @@ enum nl80211_plink_state { MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 }; +/** + * enum nl80211_plink_action - actions to perform in mesh peers + * + * @NL80211_PLINK_ACTION_NO_ACTION: perform no action + * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment + * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer + * @NUM_NL80211_PLINK_ACTIONS: number of possible actions + */ +enum plink_actions { + NL80211_PLINK_ACTION_NO_ACTION, + NL80211_PLINK_ACTION_OPEN, + NL80211_PLINK_ACTION_BLOCK, + + NUM_NL80211_PLINK_ACTIONS, +}; + + #define NL80211_KCK_LEN 16 #define NL80211_KEK_LEN 16 #define NL80211_REPLAY_CTR_LEN 8 @@ -3051,11 +4076,8 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested * to work properly to suppport receiving regulatory hints from * cellular base stations. - * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active - * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel - * in the interface combinations, even when it's only used for scan - * and remain-on-channel. This could be due to, for example, the - * remain-on-channel implementation requiring a channel context. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only + * here to reserve the value for API/ABI compatibility) * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station * mode @@ -3066,6 +4088,75 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform * OBSS scans and generate 20/40 BSS coex reports. This flag is used only * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. + * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window + * setting + * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic + * powersave + * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state + * transitions for AP clients. Without this flag (and if the driver + * doesn't have the AP SME in the device) the driver supports adding + * stations only when they're associated and adds them in associated + * state (to later be transitioned into authorized), with this flag + * they should be added before even sending the authentication reply + * and then transitioned into authenticated, associated and authorized + * states using station flags. + * Note that even for drivers that support this, the default is to add + * stations in authenticated/associated state, so to add unauthenticated + * stations the authenticated/associated bits have to be set in the mask. + * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits + * (HT40, VHT 80/160 MHz) if this flag is set + * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh + * Peering Management entity which may be implemented by registering for + * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is + * still generated by the driver. + * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor + * interface. An active monitor interface behaves like a normal monitor + * interface, but gets added to the driver. It ensures that incoming + * unicast packets directed at the configured interface address get ACKed. + * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic + * channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the + * lifetime of a BSS. + * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter + * Set IE to probe requests. + * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE + * to probe requests. + * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period + * requests sent to it by an AP. + * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the + * current tx power value into the TPC Report IE in the spectrum + * management TPC Report action frame, and in the Radio Measurement Link + * Measurement Report action frame. + * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout + * estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used + * to enable dynack. + * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial + * multiplexing powersave, ie. can turn off all but one chain + * even on HT connections that should be using more chains. + * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial + * multiplexing powersave, ie. can turn off all but one chain + * and then wake the rest up as required after, for example, + * rts/cts handshake. + * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM + * TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS + * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it + * needs to be able to handle Block-Ack agreements and other things. + * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring + * the vif's MAC address upon creation. + * See 'macaddr' field in the vif_params (cfg80211.h). + * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when + * operating as a TDLS peer. + * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a + * random MAC address during scan (if the device is unassociated); the + * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC + * address mask/value will be used. + * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports + * using a random MAC address for every scan iteration during scheduled + * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may + * be set for scheduled scan and the MAC address mask/value will be used. + * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a + * random MAC address for every scan iteration during "net detect", i.e. + * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may + * be set for scheduled scan and the MAC address mask/value will be used. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3079,6 +4170,27 @@ enum nl80211_feature_flags { NL80211_FEATURE_AP_SCAN = 1 << 8, NL80211_FEATURE_VIF_TXPOWER = 1 << 9, NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, + NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, + NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, + /* bit 13 is reserved */ + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, + NL80211_FEATURE_USERSPACE_MPM = 1 << 16, + NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18, + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 1 << 19, + NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 1 << 20, + NL80211_FEATURE_QUIET = 1 << 21, + NL80211_FEATURE_TX_POWER_INSERTION = 1 << 22, + NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23, + NL80211_FEATURE_STATIC_SMPS = 1 << 24, + NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25, + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26, + NL80211_FEATURE_MAC_ON_CREATE = 1 << 27, + NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28, + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29, + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30, + NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31, }; /** @@ -3106,7 +4218,7 @@ enum nl80211_probe_resp_offload_support_attr { * enum nl80211_connect_failed_reason - connection request failed reasons * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be * handled by the AP is reached. - * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. */ enum nl80211_connect_failed_reason { NL80211_CONN_FAIL_MAX_CLIENTS, @@ -3127,11 +4239,177 @@ enum nl80211_connect_failed_reason { * dangerous because will destroy stations performance as a lot of frames * will be lost while scanning off-channel, therefore it must be used only * when really needed + * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or + * for scheduled scan: a different one for every scan iteration). When the + * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and + * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only + * the masked bits will be preserved from the MAC address and the remainder + * randomised. If the attributes are not given full randomisation (46 bits, + * locally administered 1, multicast 0) is assumed. + * This flag must not be requested when the feature isn't supported, check + * the nl80211 feature flags for the device. */ enum nl80211_scan_flags { NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, NL80211_SCAN_FLAG_FLUSH = 1<<1, NL80211_SCAN_FLAG_AP = 1<<2, + NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, +}; + +/** + * enum nl80211_acl_policy - access control policy + * + * Access control policy is applied on a MAC list set by + * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to + * be used with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in ACL, i.e. allow all the stations which are not listed + * in ACL to authenticate. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed + * in ACL, i.e. deny all the stations which are not listed in ACL. + */ +enum nl80211_acl_policy { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, + NL80211_ACL_POLICY_DENY_UNLESS_LISTED, +}; + +/** + * enum nl80211_smps_mode - SMPS mode + * + * Requested SMPS mode (for AP mode) + * + * @NL80211_SMPS_OFF: SMPS off (use all antennas). + * @NL80211_SMPS_STATIC: static SMPS (use a single antenna) + * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and + * turn on other antennas after CTS/RTS). + */ +enum nl80211_smps_mode { + NL80211_SMPS_OFF, + NL80211_SMPS_STATIC, + NL80211_SMPS_DYNAMIC, + + __NL80211_SMPS_AFTER_LAST, + NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_radar_event - type of radar event for DFS operation + * + * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace + * about detected radars or success of the channel available check (CAC) + * + * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is + * now unusable. + * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished, + * the channel is now available. + * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no + * change to the channel status. + * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is + * over, channel becomes usable. + */ +enum nl80211_radar_event { + NL80211_RADAR_DETECTED, + NL80211_RADAR_CAC_FINISHED, + NL80211_RADAR_CAC_ABORTED, + NL80211_RADAR_NOP_FINISHED, +}; + +/** + * enum nl80211_dfs_state - DFS states for channels + * + * Channel states used by the DFS code. + * + * @NL80211_DFS_USABLE: The channel can be used, but channel availability + * check (CAC) must be performed before using it for AP or IBSS. + * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it + * is therefore marked as not available. + * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available. + */ +enum nl80211_dfs_state { + NL80211_DFS_USABLE, + NL80211_DFS_UNAVAILABLE, + NL80211_DFS_AVAILABLE, +}; + +/** + * enum enum nl80211_protocol_features - nl80211 protocol features + * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting + * wiphy dumps (if requested by the application with the attribute + * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the + * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or + * %NL80211_ATTR_WDEV. + */ +enum nl80211_protocol_features { + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, +}; + +/** + * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers + * + * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified. + * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol. + * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol. + * @NL80211_CRIT_PROTO_APIPA: APIPA protocol. + * @NUM_NL80211_CRIT_PROTO: must be kept last. + */ +enum nl80211_crit_proto_id { + NL80211_CRIT_PROTO_UNSPEC, + NL80211_CRIT_PROTO_DHCP, + NL80211_CRIT_PROTO_EAPOL, + NL80211_CRIT_PROTO_APIPA, + /* add other protocols before this one */ + NUM_NL80211_CRIT_PROTO +}; + +/* maximum duration for critical protocol measures */ +#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ + +/** + * enum nl80211_rxmgmt_flags - flags for received management frame. + * + * Used by cfg80211_rx_mgmt() + * + * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + */ +enum nl80211_rxmgmt_flags { + NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, +}; + +/* + * If this flag is unset, the lower 24 bits are an OUI, if set + * a Linux nl80211 vendor ID is used (no such IDs are allocated + * yet, so that's not valid so far) + */ +#define NL80211_VENDOR_ID_IS_LINUX 0x80000000 + +/** + * struct nl80211_vendor_cmd_info - vendor command data + * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the + * value is a 24-bit OUI; if it is set then a separately allocated ID + * may be used, but no such IDs are allocated yet. New IDs should be + * added to this file when needed. + * @subcmd: sub-command ID for the command + */ +struct nl80211_vendor_cmd_info { + __u32 vendor_id; + __u32 subcmd; +}; + +/** + * enum nl80211_tdls_peer_capability - TDLS peer flags. + * + * Used by tdls_mgmt() to determine which conditional elements need + * to be added to TDLS Setup frames. + * + * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable. + * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable. + * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable. + */ +enum nl80211_tdls_peer_capability { + NL80211_TDLS_PEER_HT = 1<<0, + NL80211_TDLS_PEER_VHT = 1<<1, + NL80211_TDLS_PEER_WMM = 1<<2, }; #endif /* __LINUX_NL80211_H */ diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h index 74d6ce58e4b1..d3f091c392b0 100644 --- a/src/drivers/priv_netlink.h +++ b/src/drivers/priv_netlink.h @@ -68,7 +68,9 @@ ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ (struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0)) struct sockaddr_nl diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/eap_common/Makefile +++ b/src/eap_common/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 7b077cb9f590..1de13281c511 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2012, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -203,3 +203,86 @@ EapType eap_get_type(const struct wpabuf *msg) 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/src/eap_common/eap_common.h b/src/eap_common/eap_common.h index 8850c1fe55dc..e62f1676476e 100644 --- a/src/eap_common/eap_common.h +++ b/src/eap_common/eap_common.h @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2012, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,14 @@ #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); @@ -19,5 +27,7 @@ struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, 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/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 0d247c4d13a7..54f26ca38f92 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -1,6 +1,6 @@ /* * EAP server/peer: Shared EAP definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,11 +27,39 @@ struct eap_hdr { #endif /* _MSC_VER */ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, - EAP_CODE_FAILURE = 4 }; + 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 @@ -63,6 +91,7 @@ typedef enum { 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; @@ -71,14 +100,19 @@ typedef enum { enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, - EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, - EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ + 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/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c new file mode 100644 index 000000000000..4dfdb3f9c96b --- /dev/null +++ b/src/eap_common/eap_eke_common.c @@ -0,0 +1,768 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-2013, Jouni Malinen + * + * 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.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/dh_groups.h" +#include "crypto/random.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "eap_common/eap_defs.h" +#include "eap_eke_common.h" + + +static int eap_eke_dh_len(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 128; + case EAP_EKE_DHGROUP_EKE_5: + return 192; + case EAP_EKE_DHGROUP_EKE_14: + return 256; + case EAP_EKE_DHGROUP_EKE_15: + return 384; + case EAP_EKE_DHGROUP_EKE_16: + return 512; + } + + return -1; +} + + +static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) +{ + int dhlen; + + dhlen = eap_eke_dh_len(dhgroup); + if (dhlen < 0) + return -1; + if (encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + return AES_BLOCK_SIZE + dhlen; +} + + +static const struct dh_group * eap_eke_dh_group(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return dh_groups_get(2); + case EAP_EKE_DHGROUP_EKE_5: + return dh_groups_get(5); + case EAP_EKE_DHGROUP_EKE_14: + return dh_groups_get(14); + case EAP_EKE_DHGROUP_EKE_15: + return dh_groups_get(15); + case EAP_EKE_DHGROUP_EKE_16: + return dh_groups_get(16); + } + + return NULL; +} + + +static int eap_eke_dh_generator(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 5; + case EAP_EKE_DHGROUP_EKE_5: + return 31; + case EAP_EKE_DHGROUP_EKE_14: + return 11; + case EAP_EKE_DHGROUP_EKE_15: + return 5; + case EAP_EKE_DHGROUP_EKE_16: + return 5; + } + + return -1; +} + + +static int eap_eke_pnonce_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 16 + mac_len; +} + + +static int eap_eke_pnonce_ps_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 2 * 16 + mac_len; +} + + +static int eap_eke_prf_len(u8 prf) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return 20; + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return 32; + return -1; +} + + +static int eap_eke_nonce_len(u8 prf) +{ + int prf_len; + + prf_len = eap_eke_prf_len(prf); + if (prf_len < 0) + return -1; + + if (prf_len > 2 * 16) + return (prf_len + 1) / 2; + + return 16; +} + + +static int eap_eke_auth_len(u8 prf) +{ + switch (prf) { + case EAP_EKE_PRF_HMAC_SHA1: + return SHA1_MAC_LEN; + case EAP_EKE_PRF_HMAC_SHA2_256: + return SHA256_MAC_LEN; + } + + return -1; +} + + +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) +{ + int generator; + u8 gen; + const struct dh_group *dh; + size_t pub_len, i; + + generator = eap_eke_dh_generator(group); + if (generator < 0 || generator > 255) + return -1; + gen = generator; + + dh = eap_eke_dh_group(group); + if (dh == NULL) + return -1; + + /* x = random number 2 .. p-1 */ + if (random_get_bytes(ret_priv, dh->prime_len)) + return -1; + if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + ret_priv[0] = 0; + } + for (i = 0; i < dh->prime_len - 1; i++) { + if (ret_priv[i]) + break; + } + if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", + ret_priv, dh->prime_len); + + /* y = g ^ x (mod p) */ + pub_len = dh->prime_len; + if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, + dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) + return -1; + if (pub_len < dh->prime_len) { + size_t pad = dh->prime_len - pub_len; + os_memmove(ret_pub + pad, ret_pub, pub_len); + os_memset(ret_pub, 0, pad); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", + ret_pub, dh->prime_len); + + return 0; +} + + +static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len, + u8 *res) +{ + const u8 *addr[2]; + size_t len[2]; + size_t num_elem = 1; + + addr[0] = data; + len[0] = data_len; + if (data2) { + num_elem++; + addr[1] = data2; + len[1] = data2_len; + } + + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return hmac_sha1_vector(key, key_len, num_elem, addr, len, res); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + res); + return -1; +} + + +static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA1_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA1_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha1_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha1_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA1_MAC_LEN) { + os_memcpy(res, hash, SHA1_MAC_LEN); + res += SHA1_MAC_LEN; + len -= SHA1_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA256_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA256_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha256_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha256_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA256_MAC_LEN) { + os_memcpy(res, hash, SHA256_MAC_LEN); + res += SHA256_MAC_LEN; + len -= SHA256_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *res, size_t len) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res, + len); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return eap_eke_prf_hmac_sha256(key, key_len, data, data_len, + res, len); + return -1; +} + + +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 temp[EAP_EKE_MAX_HASH_LEN]; + size_t key_len = 16; /* Only AES-128-CBC is used here */ + u8 *id; + + /* temp = prf(0+, password) */ + os_memset(zeros, 0, sess->prf_len); + if (eap_eke_prf(sess->prf, zeros, sess->prf_len, + password, password_len, NULL, 0, temp) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)", + temp, sess->prf_len); + + /* key = prf+(temp, ID_S | ID_P) */ + id = os_malloc(id_s_len + id_p_len); + if (id == NULL) + return -1; + os_memcpy(id, id_s, id_s_len); + os_memcpy(id + id_s_len, id_p, id_p_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P", + id, id_s_len + id_p_len); + if (eap_eke_prfplus(sess->prf, temp, sess->prf_len, + id, id_s_len + id_p_len, key, key_len) < 0) { + os_free(id); + return -1; + } + os_free(id); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)", + key, key_len); + + return 0; +} + + +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp) +{ + u8 pub[EAP_EKE_MAX_DH_LEN]; + int dh_len; + u8 iv[AES_BLOCK_SIZE]; + + dh_len = eap_eke_dh_len(sess->dhgroup); + if (dh_len < 0) + return -1; + + /* + * DHComponent = Encr(key, y) + * + * All defined DH groups use primes that have length devisible by 16, so + * no need to do extra padding for y (= pub). + */ + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + if (random_get_bytes(iv, AES_BLOCK_SIZE)) + return -1; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)", + iv, AES_BLOCK_SIZE); + os_memcpy(pub, dhpub, dh_len); + if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0) + return -1; + os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE); + os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)", + ret_dhcomp, AES_BLOCK_SIZE + dh_len); + + return 0; +} + + +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 peer_pub[EAP_EKE_MAX_DH_LEN]; + u8 modexp[EAP_EKE_MAX_DH_LEN]; + size_t len; + const struct dh_group *dh; + + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + + dh = eap_eke_dh_group(sess->dhgroup); + if (dh == NULL) + return -1; + + /* Decrypt peer DHComponent */ + os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len); + if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey", + peer_pub, dh->prime_len); + + /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ + len = dh->prime_len; + if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, + dh->prime, dh->prime_len, modexp, &len) < 0) + return -1; + if (len < dh->prime_len) { + size_t pad = dh->prime_len - len; + os_memmove(modexp + pad, modexp, len); + os_memset(modexp, 0, pad); + } + + os_memset(zeros, 0, sess->auth_len); + if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len, + NULL, 0, sess->shared_secret) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret", + sess->shared_secret, sess->auth_len); + + return 0; +} + + +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len) +{ + u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN]; + size_t ke_len, ki_len; + u8 *data; + size_t data_len; + const char *label = "EAP-EKE Keys"; + size_t label_len; + + /* + * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P) + * Ke = encryption key + * Ki = integrity protection key + * Length of each key depends on the selected algorithms. + */ + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + ke_len = 16; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + ki_len = 20; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + ki_len = 32; + else + return -1; + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + os_memcpy(data, label, label_len); + os_memcpy(data + label_len, id_s, id_s_len); + os_memcpy(data + label_len + id_s_len, id_p, id_p_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, ke_len + ki_len) < 0) { + os_free(data); + return -1; + } + + os_memcpy(sess->ke, buf, ke_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len); + os_memcpy(sess->ki, buf + ke_len, ki_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len); + + os_free(data); + return 0; +} + + +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Ka"; + size_t label_len; + + /* + * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P | + * Nonce_S) + * Ka = authentication key + * Length of the key depends on the selected algorithms. + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, sess->ka, sess->prf_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len); + + return 0; +} + + +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Exported Keys"; + size_t label_len; + u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN]; + + /* + * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S | + * ID_P | Nonce_P | Nonce_S) + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) < + 0) { + os_free(data); + return -1; + } + os_free(data); + + os_memcpy(msk, buf, EAP_MSK_LEN); + os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN); + os_memset(buf, 0, sizeof(buf)); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN); + + return 0; +} + + +static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len, + u8 *res) +{ + if (mac == EAP_EKE_MAC_HMAC_SHA1) + return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res); + if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res); + return -1; +} + + +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len) +{ + size_t block_size, icv_len, pad; + u8 *pos, *iv, *e; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + pad = data_len % block_size; + if (pad) + pad = block_size - pad; + + if (*prot_len < block_size + data_len + pad + icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + } + pos = prot; + + if (random_get_bytes(pos, block_size)) + return -1; + iv = pos; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size); + pos += block_size; + + e = pos; + os_memcpy(pos, data, data_len); + pos += data_len; + if (pad) { + if (random_get_bytes(pos, pad)) + return -1; + pos += pad; + } + + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + return -1; + pos += icv_len; + + *prot_len = pos - prot; + return 0; +} + + +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len) +{ + size_t block_size, icv_len; + u8 icv[EAP_EKE_MAX_HASH_LEN]; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + if (prot_len < 2 * block_size + icv_len) + return -1; + if ((prot_len - icv_len) % block_size) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, + prot_len - block_size - icv_len, icv) < 0) + return -1; + if (os_memcmp_const(icv, prot + prot_len - icv_len, icv_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data"); + return -1; + } + + if (*data_len < prot_len - block_size - icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data"); + return -1; + } + + *data_len = prot_len - block_size - icv_len; + os_memcpy(data, prot + block_size, *data_len); + if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data", + data, *data_len); + + return 0; +} + + +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth", + sess->ka, sess->auth_len); + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs); + return eap_eke_prf(sess->prf, sess->ka, sess->auth_len, + (const u8 *) label, os_strlen(label), + wpabuf_head(msgs), wpabuf_len(msgs), auth); +} + + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac) +{ + sess->dhgroup = dhgroup; + sess->encr = encr; + sess->prf = prf; + sess->mac = mac; + + sess->prf_len = eap_eke_prf_len(prf); + if (sess->prf_len < 0) + return -1; + sess->nonce_len = eap_eke_nonce_len(prf); + if (sess->nonce_len < 0) + return -1; + sess->auth_len = eap_eke_auth_len(prf); + if (sess->auth_len < 0) + return -1; + sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); + if (sess->dhcomp_len < 0) + return -1; + sess->pnonce_len = eap_eke_pnonce_len(sess->mac); + if (sess->pnonce_len < 0) + return -1; + sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); + if (sess->pnonce_ps_len < 0) + return -1; + + return 0; +} + + +void eap_eke_session_clean(struct eap_eke_session *sess) +{ + os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN); + os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN); + os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN); + os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN); +} diff --git a/src/eap_common/eap_eke_common.h b/src/eap_common/eap_eke_common.h new file mode 100644 index 000000000000..a4c042225d80 --- /dev/null +++ b/src/eap_common/eap_eke_common.h @@ -0,0 +1,114 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_EKE_COMMON_H +#define EAP_EKE_COMMON_H + +/* EKE Exchange */ +#define EAP_EKE_ID 1 +#define EAP_EKE_COMMIT 2 +#define EAP_EKE_CONFIRM 3 +#define EAP_EKE_FAILURE 4 + +/* Diffie-Hellman Group Registry */ +#define EAP_EKE_DHGROUP_EKE_2 1 +#define EAP_EKE_DHGROUP_EKE_5 2 +#define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */ +#define EAP_EKE_DHGROUP_EKE_15 4 +#define EAP_EKE_DHGROUP_EKE_16 5 + +/* Encryption Algorithm Registry */ +#define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */ + +/* Pseudo Random Function Registry */ +#define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_PRF_HMAC_SHA2_256 2 + +/* Keyed Message Digest (MAC) Registry */ +#define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_MAC_HMAC_SHA2_256 2 + +/* Identity Type Registry */ +#define EAP_EKE_ID_OPAQUE 1 +#define EAP_EKE_ID_NAI 2 +#define EAP_EKE_ID_IPv4 3 +#define EAP_EKE_ID_IPv6 4 +#define EAP_EKE_ID_FQDN 5 +#define EAP_EKE_ID_DN 6 + +/* Failure-Code */ +#define EAP_EKE_FAIL_NO_ERROR 1 +#define EAP_EKE_FAIL_PROTO_ERROR 2 +#define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3 +#define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4 +#define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5 +#define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6 +#define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff + +#define EAP_EKE_MAX_DH_LEN 512 +#define EAP_EKE_MAX_HASH_LEN 32 +#define EAP_EKE_MAX_KEY_LEN 16 +#define EAP_EKE_MAX_KE_LEN 16 +#define EAP_EKE_MAX_KI_LEN 32 +#define EAP_EKE_MAX_KA_LEN 32 +#define EAP_EKE_MAX_NONCE_LEN 16 + +struct eap_eke_session { + /* Selected proposal */ + u8 dhgroup; + u8 encr; + u8 prf; + u8 mac; + + u8 shared_secret[EAP_EKE_MAX_HASH_LEN]; + u8 ke[EAP_EKE_MAX_KE_LEN]; + u8 ki[EAP_EKE_MAX_KI_LEN]; + u8 ka[EAP_EKE_MAX_KA_LEN]; + + int prf_len; + int nonce_len; + int auth_len; + int dhcomp_len; + int pnonce_len; + int pnonce_ps_len; +}; + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac); +void eap_eke_session_clean(struct eap_eke_session *sess); +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub); +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key); +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp); +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp); +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len); +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s); +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk); +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len); +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len); +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth); + +#endif /* EAP_EKE_COMMON_H */ diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c index 04b987d23725..fceb1b0adc1c 100644 --- a/src/eap_common/eap_fast_common.c +++ b/src/eap_common/eap_fast_common.c @@ -174,7 +174,7 @@ void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, - int tlv_type, u8 *pos, int len) + int tlv_type, u8 *pos, size_t len) { switch (tlv_type) { case EAP_TLV_EAP_PAYLOAD_TLV: diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h index 895561747b6b..d59a8450ba8c 100644 --- a/src/eap_common/eap_fast_common.h +++ b/src/eap_common/eap_fast_common.h @@ -102,6 +102,6 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, - int tlv_type, u8 *pos, int len); + int tlv_type, u8 *pos, size_t len); #endif /* EAP_FAST_H */ diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c index 7d106dd06232..8c7ae27b933c 100644 --- a/src/eap_common/eap_gpsk_common.c +++ b/src/eap_common/eap_gpsk_common.c @@ -284,7 +284,6 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, u8 *pk, size_t *pk_len) { u8 *seed, *pos; - size_t seed_len; int ret; wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", @@ -296,8 +295,7 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */ - seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; - seed = os_malloc(seed_len); + seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len); if (seed == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " "for key derivation"); @@ -313,17 +311,18 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, pos += EAP_GPSK_RAND_LEN; os_memcpy(pos, id_server, id_server_len); pos += id_server_len; - wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed); switch (specifier) { case EAP_GPSK_CIPHER_AES: - ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len, + ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed, msk, emsk, sk, sk_len, pk, pk_len); break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: - ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len, + ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, + pos - seed, msk, emsk, sk, sk_len); break; #endif /* EAP_GPSK_SHA256 */ @@ -340,6 +339,139 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, } +static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, + u8 *kdf_out, size_t kdf_out_len, + const u8 *psk, const u8 *seed, + size_t seed_len, u8 method_type) +{ + u8 *pos, *data; + size_t data_len; + int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, + u8 *buf, size_t len); + + gkdf = NULL; + switch (csuite_specifier) { + case EAP_GPSK_CIPHER_AES: + gkdf = eap_gpsk_gkdf_cmac; + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + gkdf = eap_gpsk_gkdf_sha256; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " + "Session-Id derivation", csuite_specifier); + return -1; + } + +#define SID_LABEL "Method ID" + /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ + data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); + pos += strlen(SID_LABEL); +#undef SID_LABEL + os_memcpy(pos, &method_type, 1); + pos += 1; + WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ + pos += 4; + WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ + pos += 2; + os_memcpy(pos, seed, seed_len); /* inputString */ + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", + data, data_len); + + if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); + + return 0; +} + + +/** + * eap_gpsk_session_id - Derive EAP-GPSK Session ID + * @psk: Pre-shared key + * @psk_len: Length of psk in bytes + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @rand_peer: 32-byte RAND_Peer + * @rand_server: 32-byte RAND_Server + * @id_peer: ID_Peer + * @id_peer_len: Length of ID_Peer + * @id_server: ID_Server + * @id_server_len: Length of ID_Server + * @method_type: EAP Authentication Method Type + * @sid: Buffer for 17-byte Session ID + * @sid_len: Buffer for returning length of Session ID + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 method_type, u8 *sid, size_t *sid_len) +{ + u8 *seed, *pos; + u8 kdf_out[16]; + int ret; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", + vendor, specifier); + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); + + /* + * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server + * (= seed) + * KS = 16, CSuite_Sel = 0x00000000 0x0001 + * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || + * CSuite_Sel || inputString) + */ + seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len); + if (seed == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " + "for Session-Id derivation"); + return -1; + } + + pos = seed; + os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_peer, id_peer_len); + pos += id_peer_len; + os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_server, id_server_len); + pos += id_server_len; + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed); + + ret = eap_gpsk_derive_mid_helper(specifier, + kdf_out, sizeof(kdf_out), + psk, seed, pos - seed, + method_type); + + sid[0] = method_type; + os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); + *sid_len = 1 + sizeof(kdf_out); + + os_free(seed); + + return ret; +} + + /** * eap_gpsk_mic_len - Get the length of the MIC * @vendor: CSuite/Vendor diff --git a/src/eap_common/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h index e3d2b6b4aa63..fbcd54732b60 100644 --- a/src/eap_common/eap_gpsk_common.h +++ b/src/eap_common/eap_gpsk_common.h @@ -53,6 +53,12 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, const u8 *id_server, size_t id_server_len, u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, u8 *pk, size_t *pk_len); +int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 method_type, u8 *sid, size_t *sid_len); size_t eap_gpsk_mic_len(int vendor, int specifier); int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, int specifier, const u8 *data, size_t len, u8 *mic); diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c index 6095fd8ad739..585c79c47052 100644 --- a/src/eap_common/eap_ikev2_common.c +++ b/src/eap_common/eap_ikev2_common.c @@ -52,22 +52,12 @@ struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; -#ifdef CCNS_PL - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id); - if (msg == NULL) { - wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " - "for fragment ack"); - return NULL; - } - wpabuf_put_u8(msg, 0); /* Flags */ -#else /* CCNS_PL */ msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " "for fragment ack"); return NULL; } -#endif /* CCNS_PL */ wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack"); @@ -110,7 +100,7 @@ int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, return -1; } - if (os_memcmp(icv, end - icv_len, icv_len) != 0) { + if (os_memcmp_const(icv, end - icv_len, icv_len) != 0) { wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV"); wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV", icv, icv_len); diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h index 329ccc4d7a1b..e7502d70c0cf 100644 --- a/src/eap_common/eap_ikev2_common.h +++ b/src/eap_common/eap_ikev2_common.h @@ -9,16 +9,9 @@ #ifndef EAP_IKEV2_COMMON_H #define EAP_IKEV2_COMMON_H -#ifdef CCNS_PL -/* incorrect bit order */ -#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01 -#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02 -#define IKEV2_FLAGS_ICV_INCLUDED 0x04 -#else /* CCNS_PL */ #define IKEV2_FLAGS_LENGTH_INCLUDED 0x80 #define IKEV2_FLAGS_MORE_FRAGMENTS 0x40 #define IKEV2_FLAGS_ICV_INCLUDED 0x20 -#endif /* CCNS_PL */ #define IKEV2_FRAGMENT_SIZE 1400 diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c index b3bbacc63ea4..0e80ef511c11 100644 --- a/src/eap_common/eap_pax_common.c +++ b/src/eap_common/eap_pax_common.c @@ -121,10 +121,11 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, * @mk: Buffer for the derived Master Key * @ck: Buffer for the derived Confirmation Key * @ick: Buffer for the derived Integrity Check Key + * @mid: Buffer for the derived Method ID * Returns: 0 on success, -1 on failure */ int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, - u8 *mk, u8 *ck, u8 *ick) + u8 *mk, u8 *ck, u8 *ick, u8 *mid) { wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", @@ -132,13 +133,16 @@ int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", - e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Method ID", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MID_LEN, mid)) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MID", mid, EAP_PAX_MID_LEN); return 0; } diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h index fb03df253fee..e6cdf4df8328 100644 --- a/src/eap_common/eap_pax_common.h +++ b/src/eap_common/eap_pax_common.h @@ -74,6 +74,7 @@ enum { #define EAP_PAX_MK_LEN 16 #define EAP_PAX_CK_LEN 16 #define EAP_PAX_ICK_LEN 16 +#define EAP_PAX_MID_LEN 16 int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, @@ -86,6 +87,6 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, const u8 *data3, size_t data3_len, u8 *mac); int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, - u8 *mk, u8 *ck, u8 *ick); + u8 *mk, u8 *ck, u8 *ick, u8 *mid); #endif /* EAP_PAX_COMMON_H */ diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 7d6e6b8898a1..631c363fb7c9 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -106,9 +106,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, case 21: nid = NID_secp521r1; break; +#ifndef OPENSSL_IS_BORINGSSL case 25: nid = NID_X9_62_prime192v1; break; +#endif /* OPENSSL_IS_BORINGSSL */ case 26: nid = NID_secp224r1; break; @@ -263,18 +265,18 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, fail: EC_GROUP_free(grp->group); grp->group = NULL; - EC_POINT_free(grp->pwe); + EC_POINT_clear_free(grp->pwe); grp->pwe = NULL; - BN_free(grp->order); + BN_clear_free(grp->order); grp->order = NULL; - BN_free(grp->prime); + BN_clear_free(grp->prime); grp->prime = NULL; ret = 1; } /* cleanliness and order.... */ - BN_free(cofactor); - BN_free(x_candidate); - BN_free(rnd); + BN_clear_free(cofactor); + BN_clear_free(x_candidate); + BN_clear_free(rnd); os_free(prfbuf); return ret; @@ -284,11 +286,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, BIGNUM *peer_scalar, BIGNUM *server_scalar, u8 *confirm_peer, u8 *confirm_server, - u32 *ciphersuite, u8 *msk, u8 *emsk) + u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) { struct crypto_hash *hash; u8 mk[SHA256_MAC_LEN], *cruft; - u8 session_id[SHA256_MAC_LEN + 1]; u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; int offset; diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index 816e58ccb3d0..c54c4414f11f 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -59,7 +59,7 @@ struct eap_pwd_id { int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, int, u8 *); int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, - u8 *, u8 *, u32 *, u8 *, u8 *); + u8 *, u8 *, u32 *, u8 *, u8 *, u8 *); struct crypto_hash * eap_pwd_h_init(void); void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c index e1773bf1ace9..2adc3b376a8e 100644 --- a/src/eap_common/eap_sim_common.c +++ b/src/eap_common/eap_sim_common.c @@ -198,7 +198,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, hmac, EAP_SIM_MAC_LEN); os_free(tmp); - return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; + return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } @@ -393,7 +393,7 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, hmac, EAP_SIM_MAC_LEN); os_free(tmp); - return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; + return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } @@ -893,7 +893,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) { wpa_printf(MSG_DEBUG, "EAP-AKA': Too many " "AT_KDF attributes - ignore this"); - continue; + break; } attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos); attr->kdf_count++; @@ -972,7 +972,6 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, struct eap_sim_msg { struct wpabuf *buf; size_t mac, iv, encr; /* index from buf */ - int type; }; @@ -986,7 +985,6 @@ struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) if (msg == NULL) return NULL; - msg->type = type; msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN); if (msg->buf == NULL) { os_free(msg); @@ -1006,7 +1004,8 @@ struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) } -struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, +struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type, + const u8 *k_aut, const u8 *extra, size_t extra_len) { struct eap_hdr *eap; @@ -1019,7 +1018,7 @@ struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, eap->length = host_to_be16(wpabuf_len(msg->buf)); #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) - if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) { + if (k_aut && msg->mac && type == EAP_TYPE_AKA_PRIME) { eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) wpabuf_mhead(msg->buf) + diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h index 6021bd2e2bae..daeb0e2da0c4 100644 --- a/src/eap_common/eap_sim_common.h +++ b/src/eap_common/eap_sim_common.h @@ -211,7 +211,8 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, struct eap_sim_msg; struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); -struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, +struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type, + const u8 *k_aut, const u8 *extra, size_t extra_len); void eap_sim_msg_free(struct eap_sim_msg *msg); u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c index 376fcadbcec2..4f9e64eced08 100644 --- a/src/eap_common/ikev2_common.c +++ b/src/eap_common/ikev2_common.c @@ -21,7 +21,7 @@ static struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_MD5_96, 16, 12 } }; -#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0])) +#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) static struct ikev2_prf_alg ikev2_prf_algs[] = { @@ -29,7 +29,7 @@ static struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_MD5, 16, 16 } }; -#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0])) +#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) static struct ikev2_encr_alg ikev2_encr_algs[] = { @@ -37,7 +37,7 @@ static struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_3DES, 24, 8 } }; -#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0])) +#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs) const struct ikev2_integ_alg * ikev2_get_integ(int id) @@ -173,46 +173,12 @@ const struct ikev2_encr_alg * ikev2_get_encr(int id) } -#ifdef CCNS_PL -/* from des.c */ -struct des3_key_s { - u32 ek[3][32]; - u32 dk[3][32]; -}; - -void des3_key_setup(const u8 *key, struct des3_key_s *dkey); -void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); -void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); -#endif /* CCNS_PL */ - - int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, const u8 *plain, u8 *crypt, size_t len) { struct crypto_cipher *cipher; int encr_alg; -#ifdef CCNS_PL - if (alg == ENCR_3DES) { - struct des3_key_s des3key; - size_t i, blocks; - u8 *pos; - - /* ECB mode is used incorrectly for 3DES!? */ - if (key_len != 24) { - wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); - return -1; - } - des3_key_setup(key, &des3key); - - blocks = len / 8; - pos = crypt; - for (i = 0; i < blocks; i++) { - des3_encrypt(pos, &des3key, pos); - pos += 8; - } - } else { -#endif /* CCNS_PL */ switch (alg) { case ENCR_3DES: encr_alg = CRYPTO_CIPHER_ALG_3DES; @@ -237,9 +203,6 @@ int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, return -1; } crypto_cipher_deinit(cipher); -#ifdef CCNS_PL - } -#endif /* CCNS_PL */ return 0; } @@ -251,31 +214,6 @@ int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, struct crypto_cipher *cipher; int encr_alg; -#ifdef CCNS_PL - if (alg == ENCR_3DES) { - struct des3_key_s des3key; - size_t i, blocks; - - /* ECB mode is used incorrectly for 3DES!? */ - if (key_len != 24) { - wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); - return -1; - } - des3_key_setup(key, &des3key); - - if (len % 8) { - wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted " - "length"); - return -1; - } - blocks = len / 8; - for (i = 0; i < blocks; i++) { - des3_decrypt(crypt, &des3key, plain); - plain += 8; - crypt += 8; - } - } else { -#endif /* CCNS_PL */ switch (alg) { case ENCR_3DES: encr_alg = CRYPTO_CIPHER_ALG_3DES; @@ -300,9 +238,6 @@ int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, return -1; } crypto_cipher_deinit(cipher); -#ifdef CCNS_PL - } -#endif /* CCNS_PL */ return 0; } @@ -316,25 +251,29 @@ int ikev2_parse_payloads(struct ikev2_payloads *payloads, os_memset(payloads, 0, sizeof(*payloads)); while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) { - int plen, pdatalen; + unsigned int plen, pdatalen, left; const u8 *pdata; wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u", next_payload); - if (end - pos < (int) sizeof(*phdr)) { + if (end < pos) + return -1; + left = end - pos; + if (left < sizeof(*phdr)) { wpa_printf(MSG_INFO, "IKEV2: Too short message for " "payload header (left=%ld)", (long) (end - pos)); + return -1; } phdr = (const struct ikev2_payload_hdr *) pos; plen = WPA_GET_BE16(phdr->payload_length); - if (plen < (int) sizeof(*phdr) || pos + plen > end) { + if (plen < sizeof(*phdr) || plen > left) { wpa_printf(MSG_INFO, "IKEV2: Invalid payload header " "length %d", plen); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x" - " Payload Length: %d", + " Payload Length: %u", phdr->next_payload, phdr->flags, plen); pdata = (const u8 *) (phdr + 1); @@ -542,7 +481,7 @@ u8 * ikev2_decrypt_payload(int encr_id, int integ_id, "hash"); return NULL; } - if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) { + if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum " "Data"); return NULL; @@ -706,10 +645,6 @@ int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, keys->SK_integ_len = integ->key_len; keys->SK_encr_len = encr->key_len; keys->SK_prf_len = prf->key_len; -#ifdef CCNS_PL - /* Uses encryption key length for SK_d; should be PRF length */ - keys->SK_d_len = keys->SK_encr_len; -#endif /* CCNS_PL */ keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len + 2 * keys->SK_encr_len + 2 * keys->SK_prf_len; diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h index 45c970b6083a..8a7982ad7303 100644 --- a/src/eap_common/ikev2_common.h +++ b/src/eap_common/ikev2_common.h @@ -70,11 +70,7 @@ struct ikev2_transform { /* Current IKEv2 version from RFC 4306 */ #define IKEV2_MjVer 2 #define IKEV2_MnVer 0 -#ifdef CCNS_PL -#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4)) -#else /* CCNS_PL */ #define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer)) -#endif /* CCNS_PL */ /* IKEv2 Exchange Types */ enum { diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile index 365105611049..f79519b71849 100644 --- a/src/eap_peer/Makefile +++ b/src/eap_peer/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.so *.d + rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov install: if ls *.so >/dev/null 2>&1; then \ diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index a4c9b250696b..35433f3bd8e4 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -1,6 +1,6 @@ /* * EAP peer state machines (RFC 4137) - * Copyright (c) 2004-2012, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +23,7 @@ #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" @@ -92,6 +93,15 @@ static void eap_notify_status(struct eap_sm *sm, const char *status, } +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); @@ -144,11 +154,13 @@ 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->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; @@ -159,8 +171,9 @@ SM_STATE(EAP, INITIALIZE) eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); eapol_set_bool(sm, EAPOL_eapFail, FALSE); - os_free(sm->eapKeyData); - sm->eapKeyData = NULL; + 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 @@ -177,6 +190,9 @@ SM_STATE(EAP, INITIALIZE) 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; } @@ -340,6 +356,267 @@ nak: } +#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, 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. @@ -386,10 +663,11 @@ SM_STATE(EAP, METHOD) 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", + "methodState=%s decision=%s eapRespData=%p", ret.ignore ? "TRUE" : "FALSE", eap_sm_method_state_txt(ret.methodState), - eap_sm_decision_txt(ret.decision)); + eap_sm_decision_txt(ret.decision), + sm->eapRespData); sm->ignore = ret.ignore; if (sm->ignore) @@ -400,9 +678,22 @@ SM_STATE(EAP, METHOD) if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { - os_free(sm->eapKeyData); + 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); } } @@ -421,10 +712,13 @@ SM_STATE(EAP, SEND_RESPONSE) sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); - } else + } 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; } @@ -640,6 +934,15 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) } +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); @@ -653,6 +956,17 @@ static void eap_peer_sm_step_received(struct eap_sm *sm) (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) || @@ -684,6 +998,8 @@ static void eap_peer_sm_step_received(struct eap_sm *sm) 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); } @@ -713,8 +1029,19 @@ static void eap_peer_sm_step_local(struct eap_sm *sm) 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; @@ -891,6 +1218,7 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) 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); @@ -926,6 +1254,8 @@ static int mnc_len_from_imsi(const char *imsi) 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 */ @@ -1192,6 +1522,219 @@ static struct wpabuf * eap_sm_buildNotify(int id) } +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; @@ -1283,6 +1826,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) 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); @@ -1329,6 +1878,8 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, 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: @@ -1374,11 +1925,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, 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 */ @@ -1420,6 +1973,7 @@ void eap_peer_sm_deinit(struct eap_sm *sm) if (sm->ssl_ctx2) tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); + eap_peer_erp_free_keys(sm); os_free(sm); } @@ -1459,8 +2013,9 @@ void eap_sm_abort(struct eap_sm *sm) sm->lastRespData = NULL; wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; - os_free(sm->eapKeyData); - sm->eapKeyData = 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 @@ -1567,7 +2122,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) len = os_snprintf(buf, buflen, "EAP state=%s\n", eap_sm_state_txt(sm->EAP_state)); - if (len < 0 || (size_t) len >= buflen) + if (os_snprintf_error(buflen, len)) return 0; if (sm->selectedMethod != EAP_TYPE_NONE) { @@ -1586,7 +2141,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) ret = os_snprintf(buf + len, buflen - len, "selectedMethod=%d (EAP-%s)\n", sm->selectedMethod, name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1607,7 +2162,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) eap_sm_method_state_txt(sm->methodState), eap_sm_decision_txt(sm->decision), sm->ClientTimeout); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } @@ -1622,7 +2177,8 @@ 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; - char *txt = NULL, *tmp; + const char *txt = NULL; + char *tmp; if (sm == NULL) return; @@ -1665,6 +2221,9 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; default: return; } @@ -1775,6 +2334,17 @@ void eap_sm_request_passphrase(struct eap_sm *sm) } +/** + * 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() @@ -2001,6 +2571,8 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) 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); } @@ -2157,6 +2729,28 @@ void eap_notify_lower_layer_success(struct eap_sm *sm) } +/** + * 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() @@ -2266,6 +2860,17 @@ void eap_set_force_disabled(struct eap_sm *sm, int 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() @@ -2337,3 +2942,9 @@ 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/src/eap_peer/eap.h b/src/eap_peer/eap.h index 8bccef1b17d4..702463b9d514 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -94,7 +94,14 @@ enum eapol_bool_var { * * EAP state machines reads this value. */ - EAPOL_altReject + EAPOL_altReject, + + /** + * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start + * + * EAP state machine writes this value. + */ + EAPOL_eapTriggerStart }; /** @@ -221,10 +228,13 @@ struct eapol_callbacks { * @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); /** @@ -236,6 +246,14 @@ struct eapol_callbacks { 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 @@ -267,6 +285,14 @@ struct eap_config { * 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 * @@ -296,6 +322,7 @@ 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, @@ -303,9 +330,11 @@ struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, 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); @@ -317,6 +346,8 @@ 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 */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 59861cba1dc0..0662ae738367 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -42,7 +42,7 @@ struct eap_aka_data { u8 *last_eap_identity; size_t last_eap_identity_len; enum { - CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE + CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE } state; struct wpabuf *id_msgs; @@ -64,8 +64,6 @@ static const char * eap_aka_state_txt(int state) return "CONTINUE"; case RESULT_SUCCESS: return "RESULT_SUCCESS"; - case RESULT_FAILURE: - return "RESULT_FAILURE"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -128,6 +126,21 @@ static void * eap_aka_prime_init(struct eap_sm *sm) #endif /* EAP_AKA_PRIME */ +static void eap_aka_clear_keys(struct eap_aka_data *data, int reauth) +{ + if (!reauth) { + os_memset(data->mk, 0, EAP_SIM_MK_LEN); + os_memset(data->k_aut, 0, EAP_AKA_PRIME_K_AUT_LEN); + os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN); + os_memset(data->k_re, 0, EAP_AKA_PRIME_K_RE_LEN); + } + os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN); + os_memset(data->emsk, 0, EAP_EMSK_LEN); + os_memset(data->autn, 0, EAP_AKA_AUTN_LEN); + os_memset(data->auts, 0, EAP_AKA_AUTS_LEN); +} + + static void eap_aka_deinit(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; @@ -137,11 +150,95 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv) os_free(data->last_eap_identity); wpabuf_free(data->id_msgs); os_free(data->network_name); + eap_aka_clear_keys(data, 0); os_free(data); } } +static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data) +{ + char req[200], *pos, *end; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "UMTS-AUTH"); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN); + pos += os_snprintf(pos, end - pos, ":"); + wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN); + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + + wpa_printf(MSG_DEBUG, + "EAP-AKA: Use result from external USIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) { + pos = resp + 10; + if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts, + EAP_AKA_AUTS_LEN); + os_free(resp); + return -2; + } + + if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 10; + wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN); + + if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN); + pos += EAP_AKA_IK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN); + pos += EAP_AKA_CK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + data->res_len = os_strlen(pos) / 2; + if (data->res_len > EAP_AKA_RES_MAX_LEN) { + data->res_len = 0; + goto invalid; + } + if (hexstr2bin(pos, data->res, data->res_len) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len); + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) { struct eap_peer_config *conf; @@ -151,6 +248,14 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_aka_ext_sim_result(sm, data, conf); + else + return eap_aka_ext_sim_req(sm, data); + } + if (conf->pcsc) { return scard_umts_auth(sm->scard_ctx, data->rand, data->autn, data->res, &data->res_len, @@ -205,7 +310,7 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) { u8 autn[EAP_AKA_AUTN_LEN]; os_memset(autn, '1', EAP_AKA_AUTN_LEN); - if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { + if (os_memcmp_const(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " "with expected value"); return -1; @@ -225,7 +330,7 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) #else /* CONFIG_USIM_HARDCODED */ - wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " + wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm " "enabled"); return -1; @@ -420,7 +525,7 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, #endif /* EAP_AKA_PRIME */ sha1_vector(1, &addr, &len, hash); - if (os_memcmp(hash, checkcode, hash_len) != 0) { + if (os_memcmp_const(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } @@ -443,7 +548,7 @@ static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -460,7 +565,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, "(id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -479,7 +584,7 @@ static struct wpabuf * eap_aka_synchronization_failure( wpa_printf(MSG_DEBUG, " AT_AUTS"); eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, EAP_AKA_AUTS_LEN); - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -523,7 +628,7 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, identity, identity_len); } - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -545,7 +650,8 @@ static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); + return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "", + 0); } @@ -587,7 +693,7 @@ static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, nonce_s, + return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } @@ -621,7 +727,7 @@ static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } - return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); + return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0); } @@ -701,7 +807,7 @@ static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, EAP_AKA_SUBTYPE_CHALLENGE); wpa_printf(MSG_DEBUG, " AT_KDF"); eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -861,6 +967,9 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); return eap_aka_synchronization_failure(data, id); + } else if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); + return NULL; } else if (res) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); return eap_aka_client_error(data, id, @@ -931,7 +1040,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, if (data->result_ind && attr->result_ind) data->use_result_ind = 1; - if (data->state != FAILURE && data->state != RESULT_FAILURE) { + if (data->state != FAILURE) { eap_aka_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } @@ -1147,7 +1256,7 @@ static struct wpabuf * eap_aka_process_reauthentication( if (data->result_ind && attr->result_ind) data->use_result_ind = 1; - if (data->state != FAILURE && data->state != RESULT_FAILURE) { + if (data->state != FAILURE) { eap_aka_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } @@ -1253,9 +1362,7 @@ done: */ ret->methodState = data->use_result_ind ? METHOD_DONE : METHOD_MAY_CONT; - } else if (data->state == RESULT_FAILURE) - ret->methodState = METHOD_CONT; - else if (data->state == RESULT_SUCCESS) + } else if (data->state == RESULT_SUCCESS) ret->methodState = METHOD_CONT; if (ret->methodState == METHOD_DONE) { @@ -1282,6 +1389,7 @@ static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) data->id_msgs = NULL; data->use_result_ind = 0; data->kdf_negotiation = 0; + eap_aka_clear_keys(data, 1); } @@ -1340,6 +1448,28 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = data->eap_method; + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; @@ -1374,6 +1504,7 @@ int eap_peer_aka_register(void) eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; + eap->getSessionId = eap_aka_get_session_id; eap->has_reauth_data = eap_aka_has_reauth_data; eap->deinit_for_reauth = eap_aka_deinit_for_reauth; eap->init_for_reauth = eap_aka_init_for_reauth; @@ -1404,6 +1535,7 @@ int eap_peer_aka_prime_register(void) eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; + eap->getSessionId = eap_aka_get_session_id; eap->has_reauth_data = eap_aka_has_reauth_data; eap->deinit_for_reauth = eap_aka_deinit_for_reauth; eap->init_for_reauth = eap_aka_init_for_reauth; diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index ed909198b115..2b1a1d5e4b25 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -1,6 +1,6 @@ /* * EAP peer configuration data - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -157,7 +157,7 @@ struct eap_peer_config { * * If left out, this will be asked through control interface. */ - u8 *private_key_passwd; + char *private_key_passwd; /** * dh_file - File path to DH/DSA parameters file (in PEM format) @@ -186,6 +186,10 @@ struct eap_peer_config { * 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; @@ -207,6 +211,39 @@ struct eap_peer_config { */ 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) * @@ -271,7 +308,7 @@ struct eap_peer_config { * This field is like private_key_passwd, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *private_key2_passwd; + char *private_key2_passwd; /** * dh_file2 - File path to DH/DSA parameters file (in PEM format) @@ -302,6 +339,22 @@ struct eap_peer_config { */ 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 * @@ -365,6 +418,16 @@ struct eap_peer_config { * * 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; @@ -372,7 +435,9 @@ struct eap_peer_config { * 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. + * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can + * be used to disable MSCHAPv2 password retry in authentication failure + * cases. */ char *phase2; @@ -634,6 +699,46 @@ struct eap_peer_config { * 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; }; diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c new file mode 100644 index 000000000000..9fec66c06867 --- /dev/null +++ b/src/eap_peer/eap_eke.c @@ -0,0 +1,765 @@ +/* + * EAP peer method: EAP-EKE (RFC 6124) + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_eke_common.h" + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 *serverid; + size_t serverid_len; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + u8 dhgroup; /* forced DH group or 0 to allow all supported */ + u8 encr; /* forced encryption algorithm or 0 to allow all supported */ + u8 prf; /* forced PRF or 0 to allow all supported */ + u8 mac; /* forced MAC or 0 to allow all supported */ +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + const char *phase1; + + password = eap_get_config_password(sm, &password_len); + if (!password) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->peerid = os_malloc(identity_len); + if (data->peerid == NULL) { + eap_eke_deinit(sm, data); + return NULL; + } + os_memcpy(data->peerid, identity, identity_len); + data->peerid_len = identity_len; + } + + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos; + + pos = os_strstr(phase1, "dhgroup="); + if (pos) { + data->dhgroup = atoi(pos + 8); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u", + data->dhgroup); + } + + pos = os_strstr(phase1, "encr="); + if (pos) { + data->encr = atoi(pos + 5); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u", + data->encr); + } + + pos = os_strstr(phase1, "prf="); + if (pos) { + data->prf = atoi(pos + 4); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u", + data->prf); + } + + pos = os_strstr(phase1, "mac="); + if (pos) { + data->mac = atoi(pos + 4); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u", + data->mac); + } + } + + return data; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->serverid); + os_free(data->peerid); + wpabuf_free(data->msgs); + bin_clear_free(data, sizeof(*data)); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id, + size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_RESPONSE, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int eap_eke_supp_dhgroup(u8 dhgroup) +{ + return dhgroup == EAP_EKE_DHGROUP_EKE_2 || + dhgroup == EAP_EKE_DHGROUP_EKE_5 || + dhgroup == EAP_EKE_DHGROUP_EKE_14 || + dhgroup == EAP_EKE_DHGROUP_EKE_15 || + dhgroup == EAP_EKE_DHGROUP_EKE_16; +} + + +static int eap_eke_supp_encr(u8 encr) +{ + return encr == EAP_EKE_ENCR_AES128_CBC; +} + + +static int eap_eke_supp_prf(u8 prf) +{ + return prf == EAP_EKE_PRF_HMAC_SHA1 || + prf == EAP_EKE_PRF_HMAC_SHA2_256; +} + + +static int eap_eke_supp_mac(u8 mac) +{ + return mac == EAP_EKE_MAC_HMAC_SHA1 || + mac == EAP_EKE_MAC_HMAC_SHA2_256; +} + + +static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + u32 failure_code) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", + failure_code); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + if (resp) + wpabuf_put_be32(resp, failure_code); + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + unsigned num_prop, i; + const u8 *pos, *end; + const u8 *prop = NULL; + u8 idtype; + + if (data->state != IDENTITY) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request"); + + if (payload_len < 2 + 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + pos = payload; + end = payload + payload_len; + + num_prop = *pos++; + pos++; /* Ignore Reserved field */ + + if (pos + num_prop * 4 > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", + num_prop); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + for (i = 0; i < num_prop; i++) { + const u8 *tmp = pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u", + i, pos[0], pos[1], pos[2], pos[3]); + pos += 4; + + if ((data->dhgroup && data->dhgroup != *tmp) || + !eap_eke_supp_dhgroup(*tmp)) + continue; + tmp++; + if ((data->encr && data->encr != *tmp) || + !eap_eke_supp_encr(*tmp)) + continue; + tmp++; + if ((data->prf && data->prf != *tmp) || + !eap_eke_supp_prf(*tmp)) + continue; + tmp++; + if ((data->mac && data->mac != *tmp) || + !eap_eke_supp_mac(*tmp)) + continue; + + prop = tmp - 3; + if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2], + prop[3]) < 0) { + prop = NULL; + continue; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal"); + break; + } + + if (prop == NULL) { + wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); + } + + pos += (num_prop - i - 1) * 4; + + if (pos == end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + idtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", + pos, end - pos); + os_free(data->serverid); + data->serverid = os_malloc(end - pos); + if (data->serverid == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memcpy(data->serverid, pos, end - pos); + data->serverid_len = end - pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + 2 + 4 + 1 + data->peerid_len, + EAP_EKE_ID); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpabuf_put_u8(resp, 1); /* NumProposals */ + wpabuf_put_u8(resp, 0); /* Reserved */ + wpabuf_put_data(resp, prop, 4); /* Selected Proposal */ + wpabuf_put_u8(resp, EAP_EKE_ID_NAI); + if (data->peerid) + wpabuf_put_data(resp, data->peerid, data->peerid_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); + if (data->msgs == NULL) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, COMMIT); + + return resp; +} + + +static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end, *dhcomp; + size_t prot_len; + u8 *rpos; + u8 key[EAP_EKE_MAX_KEY_LEN]; + u8 pub[EAP_EKE_MAX_DH_LEN]; + const u8 *password; + size_t password_len; + + if (data->state != COMMIT) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request"); + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PASSWD_NOT_FOUND); + } + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.dhcomp_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + /* + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + */ + if (eap_eke_derive_key(&data->sess, password, password_len, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* + * y_p = g ^ x_p (mod p) + * x_p = random number 2 .. p-1 + */ + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0) + { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_derive_ke_ki(&data->sess, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.dhcomp_len + data->sess.pnonce_len, + EAP_EKE_COMMIT); + if (resp == NULL) { + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* DHComponent_P = Encr(key, y_p) */ + rpos = wpabuf_put(resp, data->sess.dhcomp_len); + if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memset(key, 0, sizeof(key)); + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + rpos, data->sess.dhcomp_len); + + if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", + wpabuf_put(resp, 0), prot_len); + wpabuf_put(resp, prot_len); + + /* TODO: CBValue */ + + if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) + < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, CONFIRM); + + return resp; +} + + +static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end; + size_t prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 auth_s[EAP_EKE_MAX_HASH_LEN]; + size_t decrypt_len; + u8 *auth; + + if (data->state != CONFIRM) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", + data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request"); + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + decrypt_len = sizeof(nonces); + if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, + nonces, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", + nonces, 2 * data->sess.nonce_len); + if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + os_memcpy(data->nonce_s, nonces + data->sess.nonce_len, + data->sess.nonce_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) + { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); + if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.pnonce_len + data->sess.prf_len, + EAP_EKE_CONFIRM); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put(resp, prot_len); + + auth = wpabuf_put(resp, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); + + if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request"); + + if (payload_len < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + } else { + u32 code; + code = WPA_GET_BE32(payload); + wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); + } + + return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); +} + + +static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_eke_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *end; + size_t len; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + end = pos + len; + eke_exch = *pos++; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (eke_exch) { + case EAP_EKE_ID: + resp = eap_eke_process_id(data, ret, reqData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + resp = eap_eke_process_commit(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_CONFIRM: + resp = eap_eke_process_confirm(data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_FAILURE: + resp = eap_eke_process_failure(data, ret, reqData, + pos, end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) + ret->allowNotifications = FALSE; + + return resp; +} + + +static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->deinit = eap_eke_deinit; + eap->process = eap_eke_process; + eap->isKeyAvailable = eap_eke_isKeyAvailable; + eap->getKey = eap_eke_getKey; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 7ca5288ca26e..68d7fba8892e 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -53,6 +53,8 @@ struct eap_fast_data { int session_ticket_used; u8 key_data[EAP_FAST_KEY_LEN]; + u8 *session_id; + size_t id_len; u8 emsk[EAP_EMSK_LEN]; int success; @@ -147,14 +149,16 @@ static void * eap_fast_init(struct eap_sm *sm) struct eap_fast_data *data; struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->fast_version = EAP_FAST_VERSION; data->max_pac_list_len = 10; - if (config && config->phase1 && - eap_fast_parse_phase1(data, config->phase1) < 0) { + if (config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) { eap_fast_deinit(sm, data); return NULL; } @@ -194,14 +198,22 @@ static void * eap_fast_init(struct eap_sm *sm) "workarounds"); } + if (!config->pac_file) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured"); + eap_fast_deinit(sm, data); + return NULL; + } + if (data->use_pac_binary_format && eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } if (!data->use_pac_binary_format && eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } @@ -238,6 +250,9 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) pac = pac->next; eap_fast_free_pac(prev); } + os_memset(data->key_data, 0, EAP_FAST_KEY_LEN); + os_memset(data->emsk, 0, EAP_EMSK_LEN); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -754,7 +769,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( "MAC calculation", (u8 *) _bind, bind_len); hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len, _bind->compound_mac); - res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac)); + res = os_memcmp_const(cmac, _bind->compound_mac, sizeof(cmac)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC", cmac, sizeof(cmac)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", @@ -785,6 +800,21 @@ static struct wpabuf * eap_fast_process_crypto_binding( return NULL; } + if (!data->anon_provisioning && data->phase2_success) { + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id( + sm, &data->ssl, EAP_TYPE_FAST, &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " + "Session-Id"); + wpabuf_free(resp); + return NULL; + } + } + pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv)); eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *) pos, _bind, cmk); @@ -1029,6 +1059,7 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- Provisioning completed successfully"); + sm->expected_failure = 1; } else { /* * This is PAC refreshing, i.e., normal authentication that is @@ -1051,7 +1082,8 @@ static int eap_fast_parse_decrypted(struct wpabuf *decrypted, struct eap_fast_tlv_parse *tlv, struct wpabuf **resp) { - int mandatory, tlv_type, len, res; + int mandatory, tlv_type, res; + size_t len; u8 *pos, *end; os_memset(tlv, 0, sizeof(*tlv)); @@ -1065,13 +1097,14 @@ static int eap_fast_parse_decrypted(struct wpabuf *decrypted, pos += 2; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) { + if (len > (size_t) (end - pos)) { wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " - "TLV type %d length %d%s", - tlv_type, len, mandatory ? " (mandatory)" : ""); + "TLV type %d length %u%s", + tlv_type, (unsigned int) len, + mandatory ? " (mandatory)" : ""); res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); if (res == -2) @@ -1226,6 +1259,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, "provisioning completed successfully."); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; + sm->expected_failure = 1; } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " "completed successfully."); @@ -1604,6 +1638,10 @@ static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) os_free(data); return NULL; } + os_memset(data->key_data, 0, EAP_FAST_KEY_LEN); + os_memset(data->emsk, 0, EAP_EMSK_LEN); + os_free(data->session_id); + data->session_id = NULL; if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); @@ -1628,7 +1666,7 @@ static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, ret = os_snprintf(buf + len, buflen - len, "EAP-FAST Phase2 method=%s\n", data->phase2_method->name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } @@ -1662,6 +1700,25 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *id; + + if (!data->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_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; @@ -1696,6 +1753,7 @@ int eap_peer_fast_register(void) eap->process = eap_fast_process; eap->isKeyAvailable = eap_fast_isKeyAvailable; eap->getKey = eap_fast_getKey; + eap->getSessionId = eap_fast_get_session_id; eap->get_status = eap_fast_get_status; #if 0 eap->has_reauth_data = eap_fast_has_reauth_data; diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index 8c480b967974..89e604ecf84b 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -330,6 +330,8 @@ static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root, static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac, char *pos) { + if (!pos) + return "Cannot parse pac type"; pac->pac_type = atoi(pos); if (pac->pac_type != PAC_TYPE_TUNNEL_PAC && pac->pac_type != PAC_TYPE_USER_AUTHORIZATION && @@ -502,28 +504,28 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, end = *buf + *buf_len; ret = os_snprintf(*pos, end - *pos, "%s=", field); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); ret = os_snprintf(*pos, end - *pos, "\n"); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; if (txt) { ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; for (i = 0; i < len; i++) { ret = os_snprintf(*pos, end - *pos, "%c", data[i]); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; } ret = os_snprintf(*pos, end - *pos, "\n"); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; } @@ -576,7 +578,7 @@ static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, ret = os_snprintf(*pos, *buf + *buf_len - *pos, "START\nPAC-Type=%d\n", pac->pac_type); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + if (os_snprintf_error(*buf + *buf_len - *pos, ret)) return -1; *pos += ret; @@ -598,7 +600,7 @@ static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, return -1; } ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + if (os_snprintf_error(*buf + *buf_len - *pos, ret)) return -1; *pos += ret; @@ -630,7 +632,7 @@ int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, return -1; ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); - if (ret < 0 || ret >= buf + buf_len - pos) { + if (os_snprintf_error(buf + buf_len - pos, ret)) { os_free(buf); return -1; } @@ -712,7 +714,7 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) pos += 2; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > (unsigned int) (end - pos)) break; if (type == PAC_TYPE_A_ID) { @@ -797,7 +799,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, pos = buf + 6; end = buf + len; while (pos < end) { - if (end - pos < 2 + 32 + 2 + 2) + u16 val; + + if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) goto parse_fail; pac = os_zalloc(sizeof(*pac)); @@ -808,19 +812,23 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, pos += 2; os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN); pos += EAP_FAST_PAC_KEY_LEN; - pac->pac_opaque_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (pos + pac->pac_opaque_len + 2 > end) + if (val > end - pos) goto parse_fail; + pac->pac_opaque_len = val; pac->pac_opaque = os_malloc(pac->pac_opaque_len); if (pac->pac_opaque == NULL) goto parse_fail; os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); pos += pac->pac_opaque_len; - pac->pac_info_len = WPA_GET_BE16(pos); + if (2 > end - pos) + goto parse_fail; + val = WPA_GET_BE16(pos); pos += 2; - if (pos + pac->pac_info_len > end) + if (val > end - pos) goto parse_fail; + pac->pac_info_len = val; pac->pac_info = os_malloc(pac->pac_info_len); if (pac->pac_info == NULL) goto parse_fail; diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index 2bd0d480f8e4..c54bf116477c 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-GPSK (RFC 5433) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,8 +23,8 @@ struct eap_gpsk_data { size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; - u8 session_id; - int session_id_set; + u8 session_id[128]; + size_t id_len; u8 *id_peer; size_t id_peer_len; u8 *id_server; @@ -33,6 +33,7 @@ struct eap_gpsk_data { int specifier; /* CSuite/Specifier */ u8 *psk; size_t psk_len; + u16 forced_cipher; /* force cipher or 0 to allow all supported */ }; @@ -80,6 +81,7 @@ static void * eap_gpsk_init(struct eap_sm *sm) struct eap_gpsk_data *data; const u8 *identity, *password; size_t identity_len, password_len; + const char *phase1; password = eap_get_config_password(sm, &password_len); if (password == NULL) { @@ -103,6 +105,18 @@ static void * eap_gpsk_init(struct eap_sm *sm) data->id_peer_len = identity_len; } + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos; + + pos = os_strstr(phase1, "cipher="); + if (pos) { + data->forced_cipher = atoi(pos + 7); + wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u", + data->forced_cipher); + } + } + data->psk = os_malloc(password_len); if (data->psk == NULL) { eap_gpsk_deinit(sm, data); @@ -120,8 +134,11 @@ static void eap_gpsk_deinit(struct eap_sm *sm, void *priv) struct eap_gpsk_data *data = priv; os_free(data->id_server); os_free(data->id_peer); - os_free(data->psk); - os_free(data); + if (data->psk) { + os_memset(data->psk, 0, data->psk_len); + os_free(data->psk); + } + bin_clear_free(data, sizeof(*data)); } @@ -195,7 +212,9 @@ static int eap_gpsk_select_csuite(struct eap_sm *sm, i, vendor, specifier); if (data->vendor == EAP_GPSK_VENDOR_IETF && data->specifier == EAP_GPSK_CIPHER_RESERVED && - eap_gpsk_supported_ciphersuite(vendor, specifier)) { + eap_gpsk_supported_ciphersuite(vendor, specifier) && + (!data->forced_cipher || data->forced_cipher == specifier)) + { data->vendor = vendor; data->specifier = specifier; } @@ -220,6 +239,8 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, size_t *list_len, const u8 *pos, const u8 *end) { + size_t len; + if (pos == NULL) return NULL; @@ -227,23 +248,25 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); return NULL; } - *list_len = WPA_GET_BE16(pos); + len = WPA_GET_BE16(pos); pos += 2; - if (end - pos < (int) *list_len) { + if (len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); return NULL; } - if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) { + if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", - (unsigned long) *list_len); + (unsigned long) len); return NULL; } - *list = pos; - pos += *list_len; - if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0) + if (eap_gpsk_select_csuite(sm, data, pos, len) < 0) return NULL; + *list = pos; + *list_len = len; + pos += len; + return pos; } @@ -273,6 +296,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, &csuite_list_len, pos, end); if (pos == NULL) { + ret->methodState = METHOD_DONE; eap_gpsk_state(data, FAILURE); return NULL; } @@ -354,6 +378,21 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, return NULL; } + if (eap_gpsk_derive_session_id(data->psk, data->psk_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + data->id_server, data->id_server_len, + EAP_TYPE_GPSK, + data->session_id, &data->id_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", + data->session_id, data->id_len); + /* No PD_Payload_1 */ wpabuf_put_be16(resp, 0); @@ -529,7 +568,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); return NULL; } - if (os_memcmp(mic, pos, miclen) != 0) { + if (os_memcmp_const(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); @@ -708,6 +747,24 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(data->id_len); + if (sid == NULL) + return NULL; + os_memcpy(sid, data->session_id, data->id_len); + *len = data->id_len; + + return sid; +} + + int eap_peer_gpsk_register(void) { struct eap_method *eap; @@ -724,6 +781,7 @@ int eap_peer_gpsk_register(void) eap->isKeyAvailable = eap_gpsk_isKeyAvailable; eap->getKey = eap_gpsk_getKey; eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index dd943174e444..2d7fdea22774 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -1,6 +1,6 @@ /* * EAP peer state machines internal structures (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define EAP_I_H #include "wpabuf.h" +#include "utils/list.h" #include "eap_peer/eap.h" #include "eap_common/eap_common.h" @@ -261,9 +262,32 @@ struct eap_method { * 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 */ @@ -298,6 +322,8 @@ struct eap_sm { 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; @@ -306,6 +332,8 @@ struct eap_sm { 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; @@ -330,9 +358,16 @@ struct eap_sm { 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); diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index a227f8b14ed0..b5ef71bac3ba 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -1,6 +1,6 @@ /* * EAP-IKEv2 peer (RFC 5106) - * Copyright (c) 2007, Jouni Malinen + * Copyright (c) 2007-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -60,6 +60,7 @@ static void * eap_ikev2_init(struct eap_sm *sm) struct eap_ikev2_data *data; const u8 *identity, *password; size_t identity_len, password_len; + int fragment_size; identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) { @@ -71,7 +72,11 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = WAIT_START; - data->fragment_size = IKEV2_FRAGMENT_SIZE; + fragment_size = eap_get_config_fragment_size(sm); + if (fragment_size <= 0) + data->fragment_size = IKEV2_FRAGMENT_SIZE; + else + data->fragment_size = fragment_size; data->ikev2.state = SA_INIT; data->ikev2.peer_auth = PEER_AUTH_SECRET; data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); @@ -108,7 +113,7 @@ static void eap_ikev2_deinit(struct eap_sm *sm, void *priv) wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); ikev2_responder_deinit(&data->ikev2); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -149,12 +154,6 @@ static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, send_len -= 4; } } -#ifdef CCNS_PL - /* Some issues figuring out the length of the message if Message Length - * field not included?! */ - if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) - flags |= IKEV2_FLAGS_LENGTH_INCLUDED; -#endif /* CCNS_PL */ plen = 1 + send_len; if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) @@ -246,7 +245,8 @@ static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, static int eap_ikev2_process_icv(struct eap_ikev2_data *data, const struct wpabuf *reqData, - u8 flags, const u8 *pos, const u8 **end) + u8 flags, const u8 *pos, const u8 **end, + int frag_ack) { if (flags & IKEV2_FLAGS_ICV_INCLUDED) { int icv_len = eap_ikev2_validate_icv( @@ -256,7 +256,7 @@ static int eap_ikev2_process_icv(struct eap_ikev2_data *data, return -1; /* Hide Integrity Checksum Data from further processing */ *end -= icv_len; - } else if (data->keys_ready) { + } else if (data->keys_ready && !frag_ack) { wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " "included integrity checksum"); return -1; @@ -301,6 +301,13 @@ static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, if (data->in_buf == NULL) { /* First fragment of the message */ + if (message_length > 50000) { + /* Limit maximum memory allocation */ + wpa_printf(MSG_DEBUG, + "EAP-IKEV2: Ignore too long message"); + ret->ignore = TRUE; + return NULL; + } data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " @@ -315,6 +322,7 @@ static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, (unsigned long) wpabuf_tailroom(data->in_buf)); } + ret->ignore = FALSE; return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE); } @@ -346,7 +354,9 @@ static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv, else flags = *pos++; - if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) { + if (eap_ikev2_process_icv(data, reqData, flags, pos, &end, + data->state == WAIT_FRAG_ACK && len == 0) < 0) + { ret->ignore = TRUE; return NULL; } @@ -373,12 +383,7 @@ static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv, "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { -#ifdef CCNS_PL - if (len > 1) /* Empty Flags field included in ACK */ -#else /* CCNS_PL */ - if (len != 0) -#endif /* CCNS_PL */ - { + if (len != 0) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " "in WAIT_FRAG_ACK state"); ret->ignore = TRUE; @@ -475,6 +480,36 @@ static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *sid; + size_t sid_len; + size_t offset; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; + sid = os_malloc(sid_len); + if (sid) { + offset = 0; + sid[offset] = EAP_TYPE_IKEV2; + offset++; + os_memcpy(sid + offset, data->ikev2.i_nonce, + data->ikev2.i_nonce_len); + offset += data->ikev2.i_nonce_len; + os_memcpy(sid + offset, data->ikev2.r_nonce, + data->ikev2.r_nonce_len); + *len = sid_len; + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", + sid, sid_len); + } + + return sid; +} + + int eap_peer_ikev2_register(void) { struct eap_method *eap; @@ -492,6 +527,7 @@ int eap_peer_ikev2_register(void) eap->isKeyAvailable = eap_ikev2_isKeyAvailable; eap->getKey = eap_ikev2_getKey; eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c index df3401384cf9..e0f8bcf6b0df 100644 --- a/src/eap_peer/eap_leap.c +++ b/src/eap_peer/eap_leap.c @@ -244,7 +244,7 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, ret->methodState = METHOD_DONE; ret->allowNotifications = FALSE; - if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { + 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", @@ -383,6 +383,9 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) 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; } diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c index 83a1457964a2..1bdd81e1ad52 100644 --- a/src/eap_peer/eap_methods.c +++ b/src/eap_peer/eap_methods.c @@ -103,7 +103,7 @@ size_t eap_get_names(char *buf, size_t buflen) for (m = eap_methods; m; m = m->next) { ret = os_snprintf(pos, end - pos, "%s%s", m == eap_methods ? "" : " ", m->name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; } @@ -133,7 +133,7 @@ char ** eap_get_names_as_string_array(size_t *num) for (m = eap_methods; m; m = m->next) array_len++; - array = os_zalloc(sizeof(char *) * (array_len + 1)); + array = os_calloc(array_len + 1, sizeof(char *)); if (array == NULL) return NULL; diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index 4994ff1cd9a0..e35c919abce9 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -86,6 +86,7 @@ static inline int eap_peer_method_unload(struct eap_method *method) 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); @@ -105,5 +106,6 @@ 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/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index fb6c282a1625..9e486e7d18fa 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -140,7 +140,7 @@ static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) os_free(data->peer_challenge); os_free(data->auth_challenge); wpabuf_free(data->prev_challenge); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -303,18 +303,23 @@ static void eap_mschapv2_password_changed(struct eap_sm *sm, WPA_EVENT_PASSWORD_CHANGED "EAP-MSCHAPV2: Password changed successfully"); data->prev_error = 0; - os_free(config->password); + 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); + 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; } - os_free(config->new_password); + bin_clear_free(config->new_password, + config->new_password_len); } else { config->password = config->new_password; config->password_len = config->new_password_len; @@ -467,6 +472,13 @@ static int eap_mschapv2_failure_txt(struct eap_sm *sm, 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)", @@ -549,15 +561,17 @@ static struct wpabuf * eap_mschapv2_change_password( /* Encrypted-Hash */ if (pwhash) { u8 new_password_hash[16]; - nt_password_hash(new_password, new_password_len, - new_password_hash); + 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 { - old_nt_password_hash_encrypted_with_new_nt_password_hash( - new_password, new_password_len, - password, password_len, cp->encr_hash); + 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 */ @@ -594,9 +608,13 @@ static struct wpabuf * eap_mschapv2_change_password( /* Likewise, generate master_key here since we have the needed data * available. */ - nt_password_hash(new_password, new_password_len, password_hash); - hash_nt_password_hash(password_hash, password_hash_hash); - get_master_key(password_hash_hash, cp->nt_response, data->master_key); + 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 */ @@ -644,10 +662,8 @@ static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, * must allocate a large enough temporary buffer to create that since * the received message does not include nul termination. */ - buf = os_malloc(len + 1); + buf = dup_binstr(msdata, len); if (buf) { - os_memcpy(buf, msdata, len); - buf[len] = '\0'; retry = eap_mschapv2_failure_txt(sm, data, buf); os_free(buf); } diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index 7f870520712b..6d1ff208ac7e 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -38,6 +38,7 @@ struct eap_pax_data { u8 mk[EAP_PAX_MK_LEN]; u8 ck[EAP_PAX_CK_LEN]; u8 ick[EAP_PAX_ICK_LEN]; + u8 mid[EAP_PAX_MID_LEN]; }; @@ -86,7 +87,7 @@ static void eap_pax_deinit(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; os_free(data->cid); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -178,8 +179,8 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, data->rand.r.y, EAP_PAX_RAND_LEN); if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, - data->mk, data->ck, data->ick) < 0) - { + data->mk, data->ck, data->ick, + data->mid) < 0) { ret->ignore = TRUE; return NULL; } @@ -278,7 +279,7 @@ static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, NULL, 0, mac); - if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { + if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " "received"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", @@ -415,7 +416,7 @@ static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, icvbuf); } - if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { + if (os_memcmp_const(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " "message"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", @@ -501,6 +502,26 @@ static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *sid; + + if (data->state != PAX_DONE) + return NULL; + + sid = os_malloc(1 + EAP_PAX_MID_LEN); + if (sid == NULL) + return NULL; + + *len = 1 + EAP_PAX_MID_LEN; + sid[0] = EAP_TYPE_PAX; + os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN); + + return sid; +} + + int eap_peer_pax_register(void) { struct eap_method *eap; @@ -517,6 +538,7 @@ int eap_peer_pax_register(void) eap->isKeyAvailable = eap_pax_isKeyAvailable; eap->getKey = eap_pax_getKey; eap->get_emsk = eap_pax_get_emsk; + eap->getSessionId = eap_pax_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 7fff1458a8af..86a18bb866de 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -22,7 +22,6 @@ /* 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 - * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt */ #define EAP_PEAP_VERSION 1 @@ -56,6 +55,8 @@ struct eap_peap_data { 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; @@ -169,6 +170,15 @@ static void * eap_peap_init(struct eap_sm *sm) } +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; @@ -178,7 +188,8 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) data->phase2_method->deinit(sm, data->phase2_priv); os_free(data->phase2_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); - os_free(data->key_data); + eap_peap_free_key(data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -312,8 +323,6 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; - if (data->peap_version >= 2) - tlv_type |= EAP_TLV_TYPE_MANDATORY; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); @@ -423,7 +432,7 @@ static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, buf, sizeof(buf)); hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); - if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + 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", @@ -577,33 +586,6 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, } -static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) -{ - struct wpabuf *e; - struct eap_tlv_hdr *tlv; - - if (buf == NULL) - return NULL; - - /* Encapsulate EAP packet in EAP-Payload TLV */ - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); - e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); - if (e == NULL) { - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " - "for TLV encapsulation"); - wpabuf_free(buf); - return NULL; - } - tlv = wpabuf_put(e, sizeof(*tlv)); - tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | - EAP_TLV_EAP_PAYLOAD_TLV); - tlv->length = host_to_be16(wpabuf_len(buf)); - wpabuf_put_buf(e, buf); - wpabuf_free(buf); - return e; -} - - static int eap_peap_phase2_request(struct eap_sm *sm, struct eap_peap_data *data, struct eap_method_ret *ret, @@ -834,49 +816,6 @@ continue_req: in_decrypted = nmsg; } - if (data->peap_version >= 2) { - struct eap_tlv_hdr *tlv; - struct wpabuf *nmsg; - - if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " - "EAP TLV"); - wpabuf_free(in_decrypted); - return 0; - } - tlv = wpabuf_mhead(in_decrypted); - if ((be_to_host16(tlv->tlv_type) & 0x3fff) != - EAP_TLV_EAP_PAYLOAD_TLV) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); - wpabuf_free(in_decrypted); - return 0; - } - if (sizeof(*tlv) + be_to_host16(tlv->length) > - wpabuf_len(in_decrypted)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " - "length"); - wpabuf_free(in_decrypted); - return 0; - } - hdr = (struct eap_hdr *) (tlv + 1); - if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " - "EAP packet in EAP TLV"); - wpabuf_free(in_decrypted); - return 0; - } - - nmsg = wpabuf_alloc(be_to_host16(hdr->length)); - if (nmsg == NULL) { - wpabuf_free(in_decrypted); - return 0; - } - - wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); - 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 " @@ -993,11 +932,6 @@ continue_req: wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", resp); /* PEAP version changes */ - if (data->peap_version >= 2) { - resp = eap_peapv2_tlv_eap_payload(resp); - if (resp == NULL) - return -1; - } if (wpabuf_len(resp) >= 5 && wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && eap_get_type(resp) == EAP_TYPE_TLV) @@ -1080,7 +1014,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, char *label; wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS done, proceed to Phase 2"); - os_free(data->key_data); + 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 @@ -1088,7 +1022,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, * label, "client EAP encryption", instead. Use the old * label by default, but allow it to be configured with * phase1 parameter peaplabel=1. */ - if (data->peap_version > 1 || data->force_new_label) + if (data->force_new_label) label = "client PEAP encryption"; else label = "client EAP encryption"; @@ -1107,6 +1041,20 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, "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; @@ -1176,8 +1124,9 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; - os_free(data->key_data); - data->key_data = NULL; + 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; @@ -1207,7 +1156,7 @@ static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, "EAP-PEAPv%d Phase2 method=%s\n", data->peap_version, data->phase2_method->name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } @@ -1260,6 +1209,25 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +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; @@ -1279,6 +1247,7 @@ int eap_peer_peap_register(void) 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) diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h new file mode 100644 index 000000000000..23cdbe698b3c --- /dev/null +++ b/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/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c new file mode 100644 index 000000000000..d84f01234ed5 --- /dev/null +++ b/src/eap_peer/eap_proxy_dummy.c @@ -0,0 +1,77 @@ +/* + * EAP proxy - dummy implementation for build testing + * Copyright (c) 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 "eap_proxy.h" + +struct eap_proxy_sm * +eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx) +{ + return NULL; +} + + +void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy) +{ +} + + +int eap_proxy_key_available(struct eap_proxy_sm *sm) +{ + return 0; +} + + +const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm) +{ + return NULL; +} + + +int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm) +{ + return 0; +} + + +enum eap_proxy_status +eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, + int eapReqDataLen) +{ + return EAP_PROXY_FAILURE; +} + + +int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, + int verbose) +{ + return 0; +} + + +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, + size_t *imsi_len) +{ + return -1; +} + + +int eap_proxy_notify_config(struct eap_proxy_sm *sm, + struct eap_peer_config *config) +{ + return -1; +} diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c index d618fcfd68e9..f01266354e90 100644 --- a/src/eap_peer/eap_psk.c +++ b/src/eap_peer/eap_psk.c @@ -21,6 +21,7 @@ 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; @@ -75,7 +76,7 @@ static void eap_psk_deinit(struct eap_sm *sm, void *priv) struct eap_psk_data *data = priv; os_free(data->id_s); os_free(data->id_p); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -112,6 +113,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, } 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); @@ -235,7 +237,7 @@ static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, return NULL; } os_free(buf); - if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { + 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; @@ -434,6 +436,28 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) } +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; @@ -468,6 +492,7 @@ int eap_peer_psk_register(void) 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); diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 267d0a5c6976..059bbeecb72d 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -16,7 +16,8 @@ struct eap_pwd_data { enum { - PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, + SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE } state; u8 *id_peer; size_t id_peer_len; @@ -42,6 +43,7 @@ struct eap_pwd_data { u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; + u8 session_id[1 + SHA256_MAC_LEN]; BN_CTX *bnctx; }; @@ -57,6 +59,8 @@ static const char * eap_pwd_state_txt(int state) return "PWD-Commit-Req"; case PWD_Confirm_Req: return "PWD-Confirm-Req"; + case SUCCESS_ON_FRAG_COMPLETION: + return "SUCCESS_ON_FRAG_COMPLETION"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -81,6 +85,7 @@ static void * eap_pwd_init(struct eap_sm *sm) struct eap_pwd_data *data; const u8 *identity, *password; size_t identity_len, password_len; + int fragment_size; password = eap_get_config_password(sm, &password_len); if (password == NULL) { @@ -118,7 +123,7 @@ static void * eap_pwd_init(struct eap_sm *sm) if ((data->password = os_malloc(password_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); BN_CTX_free(data->bnctx); - os_free(data->id_peer); + bin_clear_free(data->id_peer, data->id_peer_len); os_free(data); return NULL; } @@ -127,7 +132,11 @@ static void * eap_pwd_init(struct eap_sm *sm) data->out_frag_pos = data->in_frag_pos = 0; data->inbuf = data->outbuf = NULL; - data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + fragment_size = eap_get_config_fragment_size(sm); + if (fragment_size <= 0) + data->mtu = 1020; /* default from RFC 5931 */ + else + data->mtu = fragment_size; data->state = PWD_ID_Req; @@ -139,24 +148,26 @@ static void eap_pwd_deinit(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; - BN_free(data->private_value); - BN_free(data->server_scalar); - BN_free(data->my_scalar); - BN_free(data->k); + BN_clear_free(data->private_value); + BN_clear_free(data->server_scalar); + BN_clear_free(data->my_scalar); + BN_clear_free(data->k); BN_CTX_free(data->bnctx); - EC_POINT_free(data->my_element); - EC_POINT_free(data->server_element); - os_free(data->id_peer); - os_free(data->id_server); - os_free(data->password); + EC_POINT_clear_free(data->my_element); + EC_POINT_clear_free(data->server_element); + bin_clear_free(data->id_peer, data->id_peer_len); + bin_clear_free(data->id_server, data->id_server_len); + bin_clear_free(data->password, data->password_len); if (data->grp) { EC_GROUP_free(data->grp->group); - EC_POINT_free(data->grp->pwe); - BN_free(data->grp->order); - BN_free(data->grp->prime); + EC_POINT_clear_free(data->grp->pwe); + BN_clear_free(data->grp->order); + BN_clear_free(data->grp->prime); os_free(data->grp); } - os_free(data); + wpabuf_free(data->inbuf); + wpabuf_free(data->outbuf); + bin_clear_free(data, sizeof(*data)); } @@ -179,6 +190,25 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + id = os_malloc(1 + SHA256_MAC_LEN); + if (id == NULL) + return NULL; + + os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); + *len = 1 + SHA256_MAC_LEN; + + return id; +} + + static void eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, struct eap_method_ret *ret, @@ -222,8 +252,8 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", data->id_server, data->id_server_len); - if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) == - NULL) { + data->grp = os_zalloc(sizeof(EAP_PWD_group)); + if (data->grp == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); eap_pwd_state(data, FAILURE); @@ -287,11 +317,15 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - BN_rand_range(data->private_value, data->grp->order); - BN_rand_range(mask, data->grp->order); - BN_add(data->my_scalar, data->private_value, mask); - BN_mod(data->my_scalar, data->my_scalar, data->grp->order, - data->bnctx); + if (BN_rand_range(data->private_value, data->grp->order) != 1 || + BN_rand_range(mask, data->grp->order) != 1 || + BN_add(data->my_scalar, data->private_value, mask) != 1 || + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx) != 1) { + wpa_printf(MSG_INFO, + "EAP-pwd (peer): unable to get randomness"); + goto fin; + } if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, data->grp->pwe, mask, data->bnctx)) { @@ -306,7 +340,7 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); goto fin; } - BN_free(mask); + BN_clear_free(mask); if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { @@ -441,11 +475,11 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, fin: os_free(scalar); os_free(element); - BN_free(x); - BN_free(y); - BN_free(cofactor); - EC_POINT_free(K); - EC_POINT_free(point); + BN_clear_free(x); + BN_clear_free(y); + BN_clear_free(cofactor); + EC_POINT_clear_free(K); + EC_POINT_clear_free(point); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else @@ -559,7 +593,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; - if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { + if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); goto fin; } @@ -637,7 +671,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, if (compute_keys(data->grp, data->bnctx, data->k, data->my_scalar, data->server_scalar, conf, ptr, - &cs, data->msk, data->emsk) < 0) { + &cs, data->msk, data->emsk, data->session_id) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " "EMSK"); goto fin; @@ -650,16 +684,15 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - os_free(cruft); - BN_free(x); - BN_free(y); - ret->methodState = METHOD_DONE; + bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); + BN_clear_free(x); + BN_clear_free(y); if (data->outbuf == NULL) { + ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; eap_pwd_state(data, FAILURE); } else { - ret->decision = DECISION_UNCOND_SUCC; - eap_pwd_state(data, SUCCESS); + eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); } } @@ -736,6 +769,11 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", data->out_frag_pos == 0 ? "last" : "next", (int) len); + if (data->state == SUCCESS_ON_FRAG_COMPLETION) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + eap_pwd_state(data, SUCCESS); + } return resp; } @@ -748,6 +786,8 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " "total length = %d", tot_len); + if (tot_len > 15000) + return NULL; data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "Out of memory to buffer " @@ -768,6 +808,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, (int) data->in_frag_pos, (int) wpabuf_len(data->inbuf)); wpabuf_free(data->inbuf); + data->inbuf = NULL; data->in_frag_pos = 0; return NULL; } @@ -819,11 +860,15 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, */ if (data->in_frag_pos) { wpabuf_free(data->inbuf); + data->inbuf = NULL; data->in_frag_pos = 0; } - if (data->outbuf == NULL) + if (data->outbuf == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; return NULL; /* generic failure */ + } /* * we have output! Do we need to fragment it? @@ -866,6 +911,11 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, wpabuf_free(data->outbuf); data->outbuf = NULL; data->out_frag_pos = 0; + if (data->state == SUCCESS_ON_FRAG_COMPLETION) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + eap_pwd_state(data, SUCCESS); + } } return resp; @@ -902,7 +952,6 @@ int eap_peer_pwd_register(void) struct eap_method *eap; int ret; - EVP_add_digest(EVP_sha256()); eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); if (eap == NULL) @@ -913,6 +962,7 @@ int eap_peer_pwd_register(void) eap->process = eap_pwd_process; eap->isKeyAvailable = eap_pwd_key_available; eap->getKey = eap_pwd_getkey; + eap->getSessionId = eap_pwd_get_session_id; eap->get_emsk = eap_pwd_get_emsk; ret = eap_peer_method_register(eap); diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index e072f4637578..7d14907433e5 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -108,7 +108,7 @@ static void eap_sake_deinit(struct eap_sm *sm, void *priv) struct eap_sake_data *data = priv; os_free(data->serverid); os_free(data->peerid); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -315,7 +315,7 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, data->peerid, data->peerid_len, 0, wpabuf_head(reqData), wpabuf_len(reqData), attr.mic_s, mic_s); - if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { + if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); eap_sake_state(data, FAILURE); ret->methodState = METHOD_DONE; @@ -452,6 +452,28 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_SAKE_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SAKE; + os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; @@ -485,6 +507,7 @@ int eap_peer_sake_register(void) eap->process = eap_sake_process; eap->isKeyAvailable = eap_sake_isKeyAvailable; eap->getKey = eap_sake_getKey; + eap->getSessionId = eap_sake_get_session_id; eap->get_emsk = eap_sake_get_emsk; ret = eap_peer_method_register(eap); diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index c936a4475303..bd06df78db4c 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -43,7 +43,7 @@ struct eap_sim_data { u8 *last_eap_identity; size_t last_eap_identity_len; enum { - CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE + CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE } state; int result_ind, use_result_ind; }; @@ -57,8 +57,6 @@ static const char * eap_sim_state_txt(int state) return "CONTINUE"; case RESULT_SUCCESS: return "RESULT_SUCCESS"; - case RESULT_FAILURE: - return "RESULT_FAILURE"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -132,6 +130,20 @@ static void * eap_sim_init(struct eap_sm *sm) } +static void eap_sim_clear_keys(struct eap_sim_data *data, int reauth) +{ + if (!reauth) { + os_memset(data->mk, 0, EAP_SIM_MK_LEN); + os_memset(data->k_aut, 0, EAP_SIM_K_AUT_LEN); + os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN); + } + os_memset(data->kc, 0, 3 * EAP_SIM_KC_LEN); + os_memset(data->sres, 0, 3 * EAP_SIM_SRES_LEN); + os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN); + os_memset(data->emsk, 0, EAP_EMSK_LEN); +} + + static void eap_sim_deinit(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; @@ -140,11 +152,86 @@ static void eap_sim_deinit(struct eap_sm *sm, void *priv) os_free(data->pseudonym); os_free(data->reauth_id); os_free(data->last_eap_identity); + eap_sim_clear_keys(data, 0); os_free(data); } } +static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) +{ + char req[200], *pos, *end; + size_t i; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "GSM-AUTH"); + for (i = 0; i < data->num_chal; i++) { + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], + GSM_RAND_LEN); + } + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + size_t i; + + wpa_printf(MSG_DEBUG, + "EAP-SIM: Use result from external SIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 9; + for (i = 0; i < data->num_chal; i++) { + wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", + data->rand[i], GSM_RAND_LEN); + + if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", + data->kc[i], EAP_SIM_KC_LEN); + pos += EAP_SIM_KC_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", + data->sres[i], EAP_SIM_SRES_LEN); + pos += EAP_SIM_SRES_LEN * 2; + if (i + 1 < data->num_chal) { + if (*pos != ':') + goto invalid; + pos++; + } + } + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) { struct eap_peer_config *conf; @@ -154,6 +241,14 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_sim_ext_sim_result(sm, data, conf); + else + return eap_sim_ext_sim_req(sm, data); + } + if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || @@ -369,7 +464,7 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR); eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); } @@ -422,7 +517,7 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, identity, identity_len); } - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); } @@ -440,7 +535,8 @@ static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres, + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, + (u8 *) data->sres, data->num_chal * EAP_SIM_SRES_LEN); } @@ -482,7 +578,7 @@ static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, } wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, nonce_s, + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } @@ -516,7 +612,7 @@ static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } - return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0); } @@ -605,6 +701,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, const u8 *identity; size_t identity_len; struct eap_sim_attrs eattr; + int res; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); data->reauth = 0; @@ -648,8 +745,13 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); data->num_chal = attr->num_chal; - - if (eap_sim_gsm_auth(sm, data)) { + + res = eap_sim_gsm_auth(sm, data); + if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); + return NULL; + } + if (res) { wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); @@ -700,7 +802,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, if (data->result_ind && attr->result_ind) data->use_result_ind = 1; - if (data->state != FAILURE && data->state != RESULT_FAILURE) { + if (data->state != FAILURE) { eap_sim_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } @@ -864,9 +966,11 @@ static struct wpabuf * eap_sim_process_reauthentication( } if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { + struct wpabuf *res; wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " "(%d <= %d)", eattr.counter, data->counter); data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current * reauth_id must not be used to start a new reauthentication. * However, since it was used in the last EAP-Response-Identity @@ -877,8 +981,11 @@ static struct wpabuf * eap_sim_process_reauthentication( data->last_eap_identity_len = data->reauth_id_len; data->reauth_id = NULL; data->reauth_id_len = 0; + + res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s); os_free(decrypted); - return eap_sim_response_reauth(data, id, 1, eattr.nonce_s); + + return res; } data->counter = eattr.counter; @@ -896,7 +1003,7 @@ static struct wpabuf * eap_sim_process_reauthentication( if (data->result_ind && attr->result_ind) data->use_result_ind = 1; - if (data->state != FAILURE && data->state != RESULT_FAILURE) { + if (data->state != FAILURE) { eap_sim_state(data, data->use_result_ind ? RESULT_SUCCESS : SUCCESS); } @@ -995,9 +1102,7 @@ done: DECISION_UNCOND_SUCC : DECISION_COND_SUCC; ret->methodState = data->use_result_ind ? METHOD_DONE : METHOD_MAY_CONT; - } else if (data->state == RESULT_FAILURE) - ret->methodState = METHOD_CONT; - else if (data->state == RESULT_SUCCESS) + } else if (data->state == RESULT_SUCCESS) ret->methodState = METHOD_CONT; if (ret->methodState == METHOD_DONE) { @@ -1020,6 +1125,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) struct eap_sim_data *data = priv; eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); data->use_result_ind = 0; + eap_sim_clear_keys(data, 1); } @@ -1084,6 +1190,29 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SIM; + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; @@ -1118,6 +1247,7 @@ int eap_peer_sim_register(void) eap->process = eap_sim_process; eap->isKeyAvailable = eap_sim_isKeyAvailable; eap->getKey = eap_sim_getKey; + eap->getSessionId = eap_sim_get_session_id; eap->has_reauth_data = eap_sim_has_reauth_data; eap->deinit_for_reauth = eap_sim_deinit_for_reauth; eap->init_for_reauth = eap_sim_init_for_reauth; diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index 061a72b10a54..5aa3fd591256 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -21,6 +21,8 @@ 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; }; @@ -96,13 +98,50 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) #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); - os_free(data->key_data); + eap_tls_free_key(data); + os_free(data->session_id); os_free(data); } @@ -151,7 +190,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, ret->methodState = METHOD_DONE; ret->decision = DECISION_UNCOND_SUCC; - os_free(data->key_data); + eap_tls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "client EAP encryption", EAP_TLS_KEY_LEN + @@ -165,6 +204,17 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, } 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"); + } } @@ -226,8 +276,9 @@ static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; - os_free(data->key_data); - data->key_data = NULL; + 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; @@ -289,6 +340,25 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +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; @@ -304,6 +374,7 @@ int eap_peer_tls_register(void) 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; @@ -346,3 +417,35 @@ int eap_peer_unauth_tls_register(void) 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/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index aedd85a79e85..8710781618ef 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -1,6 +1,6 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2012, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +23,10 @@ static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, 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); } @@ -64,6 +68,14 @@ static void eap_tls_params_flags(struct tls_connection_params *params, 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_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; } @@ -78,6 +90,8 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, 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; @@ -99,6 +113,8 @@ static void eap_tls_params_from_conf2(struct tls_connection_params *params, 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; @@ -133,6 +149,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, } 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; } /* @@ -153,6 +171,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, return -1; } + params->openssl_ciphers = config->openssl_ciphers; + return 0; } @@ -164,6 +184,10 @@ static int eap_tls_init_connection(struct eap_sm *sm, { 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 " @@ -339,6 +363,47 @@ fail: } +/** + * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + if (tls_connection_get_keys(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 @@ -731,8 +796,11 @@ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) { ret = os_snprintf(buf + len, buflen - len, - "EAP TLS cipher=%s\n", name); - if (ret < 0 || (size_t) ret >= buflen - len) + "EAP TLS cipher=%s\n" + "tls_session_reused=%d\n", + name, tls_connection_resumed(data->ssl_ctx, + data->conn)); + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } @@ -786,6 +854,10 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, 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); diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 91d3a25a0ec9..390c2165927c 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -87,6 +87,7 @@ struct eap_ssl_data { /* 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, @@ -94,6 +95,9 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, 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 u8 *in_data, size_t in_len, diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c index bc136470b334..25b9f124801a 100644 --- a/src/eap_peer/eap_tnc.c +++ b/src/eap_peer/eap_tnc.c @@ -243,7 +243,8 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, message_length = WPA_GET_BE32(pos); pos += 4; - if (message_length < (u32) (end - pos)) { + if (message_length < (u32) (end - pos) || + message_length > 75000) { wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 9360a424c799..b5c028b5276d 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -54,6 +54,8 @@ struct eap_ttls_data { int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; @@ -131,6 +133,15 @@ static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, } +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; @@ -139,7 +150,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) eap_ttls_phase2_eap_deinit(sm, data); os_free(data->phase2_eap_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); - os_free(data->key_data); + eap_ttls_free_key(data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -210,10 +222,11 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, static int eap_ttls_v0_derive_key(struct eap_sm *sm, struct eap_ttls_data *data) { - os_free(data->key_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_TLS_KEY_LEN + + EAP_EMSK_LEN); if (!data->key_data) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); return -1; @@ -221,6 +234,20 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, 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; } @@ -478,16 +505,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (sm->workaround) { - /* At least FreeRADIUS seems to be terminating - * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success - * packet. */ - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " - "allow success without tunneled response"); - ret->methodState = METHOD_MAY_CONT; - ret->decision = DECISION_COND_SUCC; - } - return 0; #else /* EAP_MSCHAPv2 */ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); @@ -978,6 +995,7 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm, resp, out_data)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " "frame"); + wpabuf_free(resp); return -1; } wpabuf_free(resp); @@ -1526,8 +1544,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; - os_free(data->key_data); - data->key_data = NULL; + 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; @@ -1553,7 +1572,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, ret = os_snprintf(buf + len, buflen - len, "EAP-TTLSv%d Phase2 method=", data->ttls_version); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; switch (data->phase2_type) { @@ -1578,7 +1597,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, ret = 0; break; } - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1612,6 +1631,44 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) } +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; @@ -1627,10 +1684,12 @@ int eap_peer_ttls_register(void) 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) diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c index 040d1e7f9a5a..b61057ee6755 100644 --- a/src/eap_peer/eap_vendor_test.c +++ b/src/eap_peer/eap_vendor_test.c @@ -1,6 +1,6 @@ /* * EAP peer method: Test method for vendor specific (expanded) EAP type - * Copyright (c) 2005-2006, Jouni Malinen + * Copyright (c) 2005-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,31 +14,36 @@ #include "common.h" #include "eap_i.h" -#ifdef TEST_PENDING_REQUEST #include "eloop.h" -#endif /* TEST_PENDING_REQUEST */ #define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 -/* #define TEST_PENDING_REQUEST */ - struct eap_vendor_test_data { enum { INIT, CONFIRM, SUCCESS } state; int first_try; + int test_pending_req; }; static void * eap_vendor_test_init(struct eap_sm *sm) { struct eap_vendor_test_data *data; + const u8 *password; + size_t password_len; + data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = INIT; data->first_try = 1; + + password = eap_get_config_password(sm, &password_len); + data->test_pending_req = password && password_len == 7 && + os_memcmp(password, "pending", 7) == 0; + return data; } @@ -50,7 +55,6 @@ static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv) } -#ifdef TEST_PENDING_REQUEST static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) { struct eap_sm *sm = eloop_ctx; @@ -58,7 +62,6 @@ static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) "request"); eap_notify_pending(sm); } -#endif /* TEST_PENDING_REQUEST */ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, @@ -98,8 +101,7 @@ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, } if (data->state == CONFIRM) { -#ifdef TEST_PENDING_REQUEST - if (data->first_try) { + if (data->test_pending_req && data->first_try) { data->first_try = 0; wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing " "pending request"); @@ -108,7 +110,6 @@ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, NULL); return NULL; } -#endif /* TEST_PENDING_REQUEST */ } ret->ignore = FALSE; diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index d007a57082b5..7ce0a53d0b29 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -77,35 +77,47 @@ static int eap_wsc_new_ap_settings(struct wps_credential *cred, else len = end - pos; if ((len & 1) || len > 2 * sizeof(cred->ssid) || - hexstr2bin(pos, cred->ssid, len / 2)) + hexstr2bin(pos, cred->ssid, len / 2)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid"); return -1; + } cred->ssid_len = len / 2; pos = os_strstr(params, "new_auth="); - if (pos == NULL) + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth"); return -1; + } if (os_strncmp(pos + 9, "OPEN", 4) == 0) cred->auth_type = WPS_AUTH_OPEN; else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) cred->auth_type = WPS_AUTH_WPAPSK; else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) cred->auth_type = WPS_AUTH_WPA2PSK; - else + else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth"); return -1; + } pos = os_strstr(params, "new_encr="); - if (pos == NULL) + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr"); return -1; + } if (os_strncmp(pos + 9, "NONE", 4) == 0) cred->encr_type = WPS_ENCR_NONE; +#ifdef CONFIG_TESTING_OPTIONS else if (os_strncmp(pos + 9, "WEP", 3) == 0) cred->encr_type = WPS_ENCR_WEP; +#endif /* CONFIG_TESTING_OPTIONS */ else if (os_strncmp(pos + 9, "TKIP", 4) == 0) cred->encr_type = WPS_ENCR_TKIP; else if (os_strncmp(pos + 9, "CCMP", 4) == 0) cred->encr_type = WPS_ENCR_AES; - else + else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr"); return -1; + } pos = os_strstr(params, "new_key="); if (pos == NULL) @@ -117,8 +129,10 @@ static int eap_wsc_new_ap_settings(struct wps_credential *cred, else len = end - pos; if ((len & 1) || len > 2 * sizeof(cred->key) || - hexstr2bin(pos, cred->key, len / 2)) + hexstr2bin(pos, cred->key, len / 2)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key"); return -1; + } cred->key_len = len / 2; return 1; @@ -132,13 +146,13 @@ static void * eap_wsc_init(struct eap_sm *sm) size_t identity_len; int registrar; struct wps_config cfg; - const char *pos; + const char *pos, *end; const char *phase1; struct wps_context *wps; struct wps_credential new_ap_settings; int res; - u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN]; int nfc = 0; + u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN]; wps = sm->wps; if (wps == NULL) { @@ -186,15 +200,8 @@ static void * eap_wsc_init(struct eap_sm *sm) while (*pos != '\0' && *pos != ' ') pos++; cfg.pin_len = pos - (const char *) cfg.pin; - if (cfg.pin_len >= WPS_OOB_DEVICE_PASSWORD_MIN_LEN * 2 && - cfg.pin_len <= WPS_OOB_DEVICE_PASSWORD_LEN * 2 && - hexstr2bin((const char *) cfg.pin, dev_pw, - cfg.pin_len / 2) == 0) { - /* Convert OOB Device Password to binary */ - cfg.pin = dev_pw; - cfg.pin_len /= 2; - } - if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) { + if (cfg.pin_len == 6 && + os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) { cfg.pin = NULL; cfg.pin_len = 0; nfc = 1; @@ -205,6 +212,15 @@ static void * eap_wsc_init(struct eap_sm *sm) cfg.pbc = 1; } + pos = os_strstr(phase1, "dev_pw_id="); + if (pos) { + u16 id = atoi(pos + 10); + if (id == DEV_PW_NFC_CONNECTION_HANDOVER) + nfc = 1; + if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER) + cfg.dev_pw_id = id; + } + if (cfg.pin == NULL && !cfg.pbc && !nfc) { wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " "configuration data"); @@ -212,13 +228,29 @@ static void * eap_wsc_init(struct eap_sm *sm) return NULL; } - pos = os_strstr(phase1, "dev_pw_id="); - if (pos && cfg.pin) - cfg.dev_pw_id = atoi(pos + 10); + pos = os_strstr(phase1, " pkhash="); + if (pos) { + size_t len; + pos += 8; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN || + hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) { + wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash"); + os_free(data); + return NULL; + } + cfg.peer_pubkey_hash = pkhash; + } res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); if (res < 0) { os_free(data); + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP " + "settings"); return NULL; } if (res == 1) { @@ -230,6 +262,7 @@ static void * eap_wsc_init(struct eap_sm *sm) data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); + wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed"); return NULL; } res = eap_get_config_fragment_size(sm); @@ -429,7 +462,7 @@ static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, message_length = WPA_GET_BE16(pos); pos += 2; - if (message_length < end - pos) { + if (message_length < end - pos || message_length > 50000) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); ret->ignore = TRUE; diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index fcf4712aced6..55ab72aee66c 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -72,27 +72,10 @@ static int ikev2_derive_keys(struct ikev2_responder_data *data) os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); pos += IKEV2_SPI_LEN; os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); -#ifdef CCNS_PL -#if __BYTE_ORDER == __LITTLE_ENDIAN - { - int i; - u8 *tmp = pos - IKEV2_SPI_LEN; - /* Incorrect byte re-ordering on little endian hosts.. */ - for (i = 0; i < IKEV2_SPI_LEN; i++) - *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i]; - for (i = 0; i < IKEV2_SPI_LEN; i++) - *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i]; - } -#endif -#endif /* CCNS_PL */ /* SKEYSEED = prf(Ni | Nr, g^ir) */ /* Use zero-padding per RFC 4306, Sect. 2.14 */ pad_len = data->dh->prime_len - wpabuf_len(shared); -#ifdef CCNS_PL - /* Shared secret is not zero-padded correctly */ - pad_len = 0; -#endif /* CCNS_PL */ pad = os_zalloc(pad_len ? pad_len : 1); if (pad == NULL) { wpabuf_free(shared); @@ -179,21 +162,12 @@ static int ikev2_parse_transform(struct ikev2_proposal_data *prop, "Transform Attr for AES"); break; } -#ifdef CCNS_PL - if (WPA_GET_BE16(pos) != 0x001d /* ?? */) { - wpa_printf(MSG_DEBUG, "IKEV2: Not a " - "Key Size attribute for " - "AES"); - break; - } -#else /* CCNS_PL */ if (WPA_GET_BE16(pos) != 0x800e) { wpa_printf(MSG_DEBUG, "IKEV2: Not a " "Key Size attribute for " "AES"); break; } -#endif /* CCNS_PL */ if (WPA_GET_BE16(pos + 2) != 128) { wpa_printf(MSG_DEBUG, "IKEV2: " "Unsupported AES key size " @@ -239,7 +213,7 @@ static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); - if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; @@ -395,7 +369,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data, } if (kei_len < 4 + 96) { - wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); + wpa_printf(MSG_INFO, "IKEV2: Too short Key Exchange Payload"); return -1; } @@ -456,14 +430,6 @@ static int ikev2_process_ni(struct ikev2_responder_data *data, return -1; } -#ifdef CCNS_PL - /* Zeros are removed incorrectly from the beginning of the nonces */ - while (ni_len > 1 && *ni == 0) { - ni_len--; - ni++; - } -#endif /* CCNS_PL */ - data->i_nonce_len = ni_len; os_memcpy(data->i_nonce, ni, ni_len); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni", @@ -599,7 +565,7 @@ static int ikev2_process_auth_secret(struct ikev2_responder_data *data, return -1; if (auth_len != prf->hash_len || - os_memcmp(auth, auth_data, auth_len) != 0) { + os_memcmp_const(auth, auth_data, auth_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", auth, auth_len); @@ -887,16 +853,7 @@ static int ikev2_build_sar1(struct ikev2_responder_data *data, phdr->flags = 0; p = wpabuf_put(msg, sizeof(*p)); -#ifdef CCNS_PL - /* Seems to require that the Proposal # is 1 even though RFC 4306 - * Sect 3.3.1 has following requirement "When a proposal is accepted, - * all of the proposal numbers in the SA payload MUST be the same and - * MUST match the number on the proposal sent that was accepted.". - */ - p->proposal_num = 1; -#else /* CCNS_PL */ p->proposal_num = data->proposal.proposal_num; -#endif /* CCNS_PL */ p->protocol_id = IKEV2_PROTOCOL_IKE; p->num_transforms = 4; @@ -906,11 +863,7 @@ static int ikev2_build_sar1(struct ikev2_responder_data *data, WPA_PUT_BE16(t->transform_id, data->proposal.encr); if (data->proposal.encr == ENCR_AES_CBC) { /* Transform Attribute: Key Len = 128 bits */ -#ifdef CCNS_PL - wpabuf_put_be16(msg, 0x001d); /* ?? */ -#else /* CCNS_PL */ wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ -#endif /* CCNS_PL */ wpabuf_put_be16(msg, 128); /* 128-bit key */ } plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; @@ -1082,11 +1035,7 @@ static int ikev2_build_notification(struct ikev2_responder_data *data, phdr = wpabuf_put(msg, sizeof(*phdr)); phdr->next_payload = next_payload; phdr->flags = 0; -#ifdef CCNS_PL - wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */ -#else /* CCNS_PL */ wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */ -#endif /* CCNS_PL */ wpabuf_put_u8(msg, 0); /* SPI Size */ wpabuf_put_be16(msg, data->error_type); @@ -1130,13 +1079,6 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) data->r_nonce_len = IKEV2_NONCE_MIN_LEN; if (random_get_bytes(data->r_nonce, data->r_nonce_len)) return NULL; -#ifdef CCNS_PL - /* Zeros are removed incorrectly from the beginning of the nonces in - * key derivation; as a workaround, make sure Nr does not start with - * zero.. */ - if (data->r_nonce[0] == 0) - data->r_nonce[0] = 1; -#endif /* CCNS_PL */ wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len); msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500); @@ -1257,6 +1199,7 @@ static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data) wpabuf_free(msg); return NULL; } + wpabuf_free(plain); data->state = IKEV2_FAILED; } else { /* HDR, N */ diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c index 37e6735efb34..9bc737076b9b 100644 --- a/src/eap_peer/mschapv2.c +++ b/src/eap_peer/mschapv2.c @@ -117,8 +117,8 @@ int mschapv2_verify_auth_response(const u8 *auth_response, buf[0] != 'S' || buf[1] != '=' || hexstr2bin((char *) (buf + 2), recv_response, MSCHAPV2_AUTH_RESPONSE_LEN) || - os_memcmp(auth_response, recv_response, - MSCHAPV2_AUTH_RESPONSE_LEN) != 0) + os_memcmp_const(auth_response, recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) != 0) return -1; return 0; } diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index f5edfd52c250..7ca956e5b235 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -13,6 +13,7 @@ #include "common.h" #include "base64.h" +#include "common/tnc.h" #include "tncc.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_defs.h" @@ -25,7 +26,9 @@ #endif /* UNICODE */ +#ifndef TNC_CONFIG_FILE #define TNC_CONFIG_FILE "/etc/tnc_config" +#endif /* TNC_CONFIG_FILE */ #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs") #define IF_TNCCS_START \ "\n" \ @@ -38,56 +41,6 @@ /* TNC IF-IMC */ -typedef unsigned long TNC_UInt32; -typedef unsigned char *TNC_BufferReference; - -typedef TNC_UInt32 TNC_IMCID; -typedef TNC_UInt32 TNC_ConnectionID; -typedef TNC_UInt32 TNC_ConnectionState; -typedef TNC_UInt32 TNC_RetryReason; -typedef TNC_UInt32 TNC_MessageType; -typedef TNC_MessageType *TNC_MessageTypeList; -typedef TNC_UInt32 TNC_VendorID; -typedef TNC_UInt32 TNC_MessageSubtype; -typedef TNC_UInt32 TNC_Version; -typedef TNC_UInt32 TNC_Result; - -typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( - TNC_IMCID imcID, - char *functionName, - void **pOutfunctionPointer); - -#define TNC_RESULT_SUCCESS 0 -#define TNC_RESULT_NOT_INITIALIZED 1 -#define TNC_RESULT_ALREADY_INITIALIZED 2 -#define TNC_RESULT_NO_COMMON_VERSION 3 -#define TNC_RESULT_CANT_RETRY 4 -#define TNC_RESULT_WONT_RETRY 5 -#define TNC_RESULT_INVALID_PARAMETER 6 -#define TNC_RESULT_CANT_RESPOND 7 -#define TNC_RESULT_ILLEGAL_OPERATION 8 -#define TNC_RESULT_OTHER 9 -#define TNC_RESULT_FATAL 10 - -#define TNC_CONNECTION_STATE_CREATE 0 -#define TNC_CONNECTION_STATE_HANDSHAKE 1 -#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 -#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 -#define TNC_CONNECTION_STATE_ACCESS_NONE 4 -#define TNC_CONNECTION_STATE_DELETE 5 - -#define TNC_IFIMC_VERSION_1 1 - -#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) -#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff) - -/* TNCC-TNCS Message Types */ -#define TNC_TNCCS_RECOMMENDATION 0x00000001 -#define TNC_TNCCS_ERROR 0x00000002 -#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 -#define TNC_TNCCS_REASONSTRINGS 0x00000004 - - /* IF-TNCCS-SOH - SSoH and SSoHR Attributes */ enum { SSOH_MS_MACHINE_INVENTORY = 1, @@ -741,12 +694,10 @@ enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; - buf = os_malloc(len + 1); + buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; - os_memcpy(buf, msg, len); - buf[len] = '\0'; start = os_strstr(buf, ""); if (start == NULL || end == NULL || start > end) { @@ -1141,8 +1092,10 @@ static int tncc_read_config(struct tncc_data *tncc) int error = 0; imc = tncc_parse_imc(pos + 4, line_end, &error); - if (error) + if (error) { + os_free(config); return -1; + } if (imc) { if (last == NULL) tncc->imc = imc; diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/eap_server/Makefile +++ b/src/eap_server/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index f2a7cd752f75..9de6cb62f517 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -1,6 +1,6 @@ /* * hostapd / EAP Full Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define EAP_H #include "common/defs.h" +#include "utils/list.h" #include "eap_common/eap_defs.h" #include "eap_server/eap_methods.h" #include "wpabuf.h" @@ -32,8 +33,11 @@ struct eap_user { * nt_password_hash() */ int phase2; int force_version; + unsigned int remediation:1; + unsigned int macacl:1; int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ + struct hostapd_radius_attr *accept_attr; }; struct eap_eapol_interface { @@ -55,6 +59,8 @@ struct eap_eapol_interface { struct wpabuf *eapReqData; u8 *eapKeyData; size_t eapKeyDataLen; + u8 *eapSessionId; + size_t eapSessionIdLen; Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */ /* AAA interface to full authenticator variables */ @@ -75,10 +81,27 @@ struct eap_eapol_interface { Boolean aaaTimeout; }; +struct eap_server_erp_key { + struct dl_list list; + size_t rRK_len; + size_t rIK_len; + u8 rRK[ERP_MAX_KEY_LEN]; + u8 rIK[ERP_MAX_KEY_LEN]; + u32 recv_seq; + u8 cryptosuite; + char keyname_nai[]; +}; + struct eapol_callbacks { int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); const char * (*get_eap_req_id_text)(void *ctx, size_t *len); + void (*log_msg)(void *ctx, const char *msg); + int (*get_erp_send_reauth_start)(void *ctx); + const char * (*get_erp_domain)(void *ctx); + struct eap_server_erp_key * (*erp_get_key)(void *ctx, + const char *keyname); + int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp); }; struct eap_config { @@ -104,6 +127,14 @@ struct eap_config { int fragment_size; int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; + int erp; + +#ifdef CONFIG_TESTING_OPTIONS + u32 tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ }; diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index f92704a11e0f..7d723091ffb5 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -88,6 +88,19 @@ struct eap_method { * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); }; /** @@ -103,7 +116,8 @@ struct eap_sm { EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, - EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2 + EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2, + EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED } EAP_state; /* Constants */ @@ -125,6 +139,7 @@ struct eap_sm { /* Short-term (not maintained between packets) */ Boolean rxResp; + Boolean rxInitiate; int respId; EapType respMethod; int respVendor; @@ -132,7 +147,7 @@ struct eap_sm { Boolean ignore; enum { DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, - DECISION_PASSTHROUGH + DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START } decision; /* Miscellaneous variables */ @@ -188,10 +203,23 @@ struct eap_sm { int fragment_size; int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; + + Boolean initiate_reauth_start_sent; + Boolean try_initiate_reauth; + int erp; + +#ifdef CONFIG_TESTING_OPTIONS + u32 tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2); +void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) +PRINTF_FORMAT(2, 3); void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len); #endif /* EAP_I_H */ diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h index bc810a9b815c..0baa3279086e 100644 --- a/src/eap_server/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -27,6 +27,7 @@ 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); @@ -45,5 +46,6 @@ 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/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 15f7e22846ec..bd919e570c82 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -1,6 +1,6 @@ /* * hostapd / EAP Full Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "eap_i.h" #include "state_machine.h" #include "common/wpa_ctrl.h" @@ -44,6 +45,73 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm); static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); +static int eap_get_erp_send_reauth_start(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_send_reauth_start) + return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx); + return 0; +} + + +static const char * eap_get_erp_domain(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_domain) + return sm->eapol_cb->get_erp_domain(sm->eapol_ctx); + return NULL; +} + + +#ifdef CONFIG_ERP + +static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm, + const char *keyname) +{ + if (sm->eapol_cb->erp_get_key) + return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname); + return NULL; +} + + +static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp) +{ + if (sm->eapol_cb->erp_add_key) + return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp); + return -1; +} + +#endif /* CONFIG_ERP */ + + +static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, + u8 id) +{ + const char *domain; + size_t plen = 1; + struct wpabuf *msg; + size_t domain_len = 0; + + domain = eap_get_erp_domain(sm); + if (domain) { + domain_len = os_strlen(domain); + plen += 2 + domain_len; + } + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen, + EAP_CODE_INITIATE, id); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, 0); /* Reserved */ + if (domain) { + /* Domain name TLV */ + wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME); + wpabuf_put_u8(msg, domain_len); + wpabuf_put_data(msg, domain, domain_len); + } + + return msg; +} + + static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) { if (src == NULL) @@ -119,6 +187,32 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, } +void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL) + return; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + sm->eapol_cb->log_msg(sm->eapol_ctx, buf); + + os_free(buf); +} + + SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); @@ -138,13 +232,17 @@ SM_STATE(EAP, INITIALIZE) eap_server_clear_identity(sm); } + sm->try_initiate_reauth = FALSE; sm->currentId = -1; sm->eap_if.eapSuccess = FALSE; sm->eap_if.eapFail = FALSE; sm->eap_if.eapTimeout = FALSE; - os_free(sm->eap_if.eapKeyData); + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; + os_free(sm->eap_if.eapSessionId); + sm->eap_if.eapSessionId = NULL; + sm->eap_if.eapSessionIdLen = 0; sm->eap_if.eapKeyAvailable = FALSE; sm->eap_if.eapRestart = FALSE; @@ -310,6 +408,95 @@ SM_STATE(EAP, METHOD_REQUEST) } +static void eap_server_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]; + const char *domain; + size_t domain_len, nai_buf_len; + struct eap_server_erp_key *erp = NULL; + int pos; + + domain = eap_get_erp_domain(sm); + if (!domain) + return; + + domain_len = os_strlen(domain); + + nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_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"); + return; + } + nai_buf_len++; /* null termination */ + erp = os_zalloc(sizeof(*erp) + nai_buf_len); + if (erp == NULL) + goto fail; + erp->recv_seq = (u32) -1; + + 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->eap_if.eapSessionId, sm->eap_if.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], domain, domain_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); + + if (eap_erp_add_key(sm, erp) == 0) { + wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", + erp->keyname_nai); + erp = NULL; + } + +fail: + bin_clear_free(emsk, emsk_len); + bin_clear_free(erp, sizeof(*erp)); +#endif /* CONFIG_ERP */ +} + + SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); @@ -320,7 +507,7 @@ SM_STATE(EAP, METHOD_RESPONSE) sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); if (sm->m->isDone(sm, sm->eap_method_priv)) { eap_sm_Policy_update(sm, NULL, 0); - os_free(sm->eap_if.eapKeyData); + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); if (sm->m->getKey) { sm->eap_if.eapKeyData = sm->m->getKey( sm, sm->eap_method_priv, @@ -329,6 +516,18 @@ SM_STATE(EAP, METHOD_RESPONSE) sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; } + os_free(sm->eap_if.eapSessionId); + sm->eap_if.eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eap_if.eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eap_if.eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eap_if.eapSessionId, + sm->eap_if.eapSessionIdLen); + } + if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId) + eap_server_erp_init(sm); sm->methodState = METHOD_END; } else { sm->methodState = METHOD_CONTINUE; @@ -343,6 +542,8 @@ SM_STATE(EAP, PROPOSE_METHOD) SM_ENTRY(EAP, PROPOSE_METHOD); + sm->try_initiate_reauth = FALSE; +try_another_method: type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) sm->currentMethod = type; @@ -360,8 +561,15 @@ SM_STATE(EAP, PROPOSE_METHOD) "method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; + goto try_another_method; } } + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); + eap_log_msg(sm, "Could not find suitable EAP method"); + sm->decision = DECISION_FAILURE; + return; + } if (sm->currentMethod == EAP_TYPE_IDENTITY || sm->currentMethod == EAP_TYPE_NOTIFICATION) sm->methodState = METHOD_CONTINUE; @@ -370,6 +578,8 @@ SM_STATE(EAP, PROPOSE_METHOD) wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", vendor, sm->currentMethod); + eap_log_msg(sm, "Propose EAP method vendor=%u method=%u", + vendor, sm->currentMethod); } @@ -456,12 +666,326 @@ SM_STATE(EAP, SUCCESS) } +SM_STATE(EAP, INITIATE_REAUTH_START) +{ + SM_ENTRY(EAP, INITIATE_REAUTH_START); + + sm->initiate_reauth_start_sent = TRUE; + sm->try_initiate_reauth = TRUE; + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, + "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm, + sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; +} + + +#ifdef CONFIG_ERP + +static void erp_send_finish_reauth(struct eap_sm *sm, + struct eap_server_erp_key *erp, u8 id, + u8 flags, u16 seq, const char *nai) +{ + size_t plen; + struct wpabuf *msg; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + u8 seed[4]; + + if (erp) { + switch (erp->cryptosuite) { + case EAP_ERP_CS_HMAC_SHA256_256: + hash_len = 32; + break; + case EAP_ERP_CS_HMAC_SHA256_128: + hash_len = 16; + break; + default: + return; + } + } else + hash_len = 0; + + plen = 1 + 2 + 2 + os_strlen(nai); + if (hash_len) + plen += 1 + hash_len; + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen, + EAP_CODE_FINISH, id); + if (msg == NULL) + return; + wpabuf_put_u8(msg, flags); + wpabuf_put_be16(msg, seq); + + wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); + wpabuf_put_u8(msg, os_strlen(nai)); + wpabuf_put_str(msg, nai); + + if (erp) { + wpabuf_put_u8(msg, erp->cryptosuite); + if (hmac_sha256(erp->rIK, erp->rIK_len, + wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { + wpabuf_free(msg); + return; + } + wpabuf_put_data(msg, hash, hash_len); + } + + wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)", + flags & 0x80 ? "failure" : "success"); + + sm->lastId = sm->currentId; + sm->currentId = id; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = msg; + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + + if (flags & 0x80) { + sm->eap_if.eapFail = TRUE; + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + MACSTR, MAC2STR(sm->peer_addr)); + return; + } + + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapKeyDataLen = 0; + sm->eap_if.eapKeyData = os_malloc(erp->rRK_len); + if (!sm->eap_if.eapKeyData) + return; + + 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->eap_if.eapKeyData, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); + bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len); + sm->eap_if.eapKeyData = NULL; + return; + } + sm->eap_if.eapKeyDataLen = erp->rRK_len; + sm->eap_if.eapKeyAvailable = TRUE; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", + sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapSuccess = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, INITIATE_RECEIVED) +{ + const u8 *pos, *end, *start, *tlvs, *hdr; + const struct eap_hdr *ehdr; + size_t len; + u8 flags; + u16 seq; + char nai[254]; + struct eap_server_erp_key *erp; + int max_len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + struct erp_tlvs parse; + u8 resp_flags = 0x80; /* default to failure; cleared on success */ + + SM_ENTRY(EAP, INITIATE_RECEIVED); + + sm->rxInitiate = FALSE; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + sm->eap_if.eapRespData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); + goto fail; + } + hdr = wpabuf_head(sm->eap_if.eapRespData); + ehdr = wpabuf_head(sm->eap_if.eapRespData); + + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len); + if (len < 4) { + wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth"); + goto fail; + } + end = pos + len; + + flags = *pos++; + seq = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); + tlvs = pos; + + /* + * 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(tlvs, end, &parse, 1) < 0) + goto fail; + + if (!parse.keyname) { + wpa_printf(MSG_DEBUG, + "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet"); + goto fail; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/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-Initiate/Re-auth"); + goto fail; + } + os_memcpy(nai, parse.keyname, parse.keyname_len); + nai[parse.keyname_len] = '\0'; + + if (!sm->eap_server) { + /* + * In passthrough case, EAP-Initiate/Re-auth replaces + * EAP Identity exchange. Use keyName-NAI as the user identity + * and forward EAP-Initiate/Re-auth to the backend + * authentication server. + */ + wpa_printf(MSG_DEBUG, + "EAP: Use keyName-NAI as user identity for backend authentication"); + eap_server_clear_identity(sm); + sm->identity = (u8 *) dup_binstr(parse.keyname, + parse.keyname_len); + if (!sm->identity) + goto fail; + sm->identity_len = parse.keyname_len; + return; + } + + erp = eap_erp_get_key(sm, nai); + if (!erp) { + wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", + nai); + goto report_error; + } + + if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) { + wpa_printf(MSG_DEBUG, + "EAP: SEQ=%u replayed (already received SEQ=%u)", + seq, erp->recv_seq); + goto fail; + } + + /* Is there enough room for Cryptosuite and Authentication Tag? */ + start = parse.keyname + parse.keyname_len; + max_len = end - start; + if (max_len < + 1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) { + wpa_printf(MSG_DEBUG, + "EAP: Not enough room for Authentication Tag"); + goto fail; + } + + switch (erp->cryptosuite) { + case EAP_ERP_CS_HMAC_SHA256_256: + if (end[-33] != erp->cryptosuite) { + wpa_printf(MSG_DEBUG, + "EAP: Different Cryptosuite used"); + goto fail; + } + hash_len = 32; + break; + case EAP_ERP_CS_HMAC_SHA256_128: + if (end[-17] != erp->cryptosuite) { + wpa_printf(MSG_DEBUG, + "EAP: Different Cryptosuite used"); + goto fail; + } + hash_len = 16; + break; + default: + hash_len = 0; + break; + } + + if (hash_len) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - hash_len, hash) < 0) + goto fail; + if (os_memcmp(end - hash_len, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag mismatch"); + goto fail; + } + } + + /* Check if any supported CS results in matching tag */ + if (!hash_len && max_len >= 1 + 32 && + end[-33] == EAP_ERP_CS_HMAC_SHA256_256) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - 32, hash) < 0) + goto fail; + if (os_memcmp(end - 32, hash, 32) == 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag match using HMAC-SHA256-256"); + hash_len = 32; + erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256; + } + } + + if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - 16, hash) < 0) + goto fail; + if (os_memcmp(end - 16, hash, 16) == 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag match using HMAC-SHA256-128"); + hash_len = 16; + erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128; + } + } + + if (!hash_len) { + wpa_printf(MSG_DEBUG, + "EAP: No supported cryptosuite matched Authentication Tag"); + goto fail; + } + end -= 1 + hash_len; + + /* + * Parse TVs/TLVs again now that we know the exact part of the buffer + * that contains them. + */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs", + tlvs, end - tlvs); + if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0) + goto fail; + + wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u", + erp->keyname_nai, seq); + erp->recv_seq = seq; + resp_flags &= ~0x80; /* R=0 - success */ + +report_error: + erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai); + return; + +fail: + sm->ignore = TRUE; +} + +#endif /* CONFIG_ERP */ + + SM_STATE(EAP, INITIALIZE_PASSTHROUGH) { SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); wpabuf_free(sm->eap_if.aaaEapRespData); sm->eap_if.aaaEapRespData = NULL; + sm->try_initiate_reauth = FALSE; } @@ -596,7 +1120,7 @@ SM_STATE(EAP, SUCCESS2) if (sm->eap_if.aaaEapKeyAvailable) { EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData); } else { - os_free(sm->eap_if.eapKeyData); + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; } @@ -655,9 +1179,14 @@ SM_STEP(EAP) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: - if (sm->eap_if.retransWhile == 0) - SM_ENTER(EAP, RETRANSMIT); - else if (sm->eap_if.eapResp) + if (sm->eap_if.retransWhile == 0) { + if (sm->try_initiate_reauth) { + sm->try_initiate_reauth = FALSE; + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, RETRANSMIT); + } + } else if (sm->eap_if.eapResp) SM_ENTER(EAP, RECEIVED); break; case EAP_RETRANSMIT: @@ -680,12 +1209,17 @@ SM_STEP(EAP) sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == sm->currentMethod))) SM_ENTER(EAP, INTEGRITY_CHECK); +#ifdef CONFIG_ERP + else if (sm->rxInitiate) + SM_ENTER(EAP, INITIATE_RECEIVED); +#endif /* CONFIG_ERP */ else { wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " "rxResp=%d respId=%d currentId=%d " "respMethod=%d currentMethod=%d", sm->rxResp, sm->respId, sm->currentId, sm->respMethod, sm->currentMethod); + eap_log_msg(sm, "Discard received EAP message"); SM_ENTER(EAP, DISCARD); } break; @@ -702,6 +1236,15 @@ SM_STEP(EAP) SM_ENTER(EAP, METHOD_RESPONSE); break; case EAP_METHOD_REQUEST: + if (sm->m == NULL) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * initialization fails. + */ + SM_ENTER(EAP, FAILURE); + break; + } SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: @@ -758,9 +1301,22 @@ SM_STEP(EAP) SM_ENTER(EAP, SUCCESS); else if (sm->decision == DECISION_PASSTHROUGH) SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); + else if (sm->decision == DECISION_INITIATE_REAUTH_START) + SM_ENTER(EAP, INITIATE_REAUTH_START); +#ifdef CONFIG_ERP + else if (sm->eap_server && sm->erp && sm->rxInitiate) + SM_ENTER(EAP, INITIATE_RECEIVED); +#endif /* CONFIG_ERP */ else SM_ENTER(EAP, PROPOSE_METHOD); break; + case EAP_INITIATE_REAUTH_START: + SM_ENTER(EAP, SEND_REQUEST); + break; + case EAP_INITIATE_RECEIVED: + if (!sm->eap_server) + SM_ENTER(EAP, SELECT_ACTION); + break; case EAP_TIMEOUT_FAILURE: break; case EAP_FAILURE: @@ -830,6 +1386,12 @@ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, { int rto, i; + if (sm->try_initiate_reauth) { + wpa_printf(MSG_DEBUG, + "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start"); + return 1; + } + if (methodTimeout) { /* * EAP method (either internal or through AAA server, provided @@ -883,6 +1445,7 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) /* parse rxResp, respId, respMethod */ sm->rxResp = FALSE; + sm->rxInitiate = FALSE; sm->respId = -1; sm->respMethod = EAP_TYPE_NONE; sm->respVendor = EAP_VENDOR_IETF; @@ -909,6 +1472,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) if (hdr->code == EAP_CODE_RESPONSE) sm->rxResp = TRUE; + else if (hdr->code == EAP_CODE_INITIATE) + sm->rxInitiate = TRUE; if (plen > sizeof(*hdr)) { u8 *pos = (u8 *) (hdr + 1); @@ -926,10 +1491,10 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) } } - wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " - "respMethod=%u respVendor=%u respVendorMethod=%u", - sm->rxResp, sm->respId, sm->respMethod, sm->respVendor, - sm->respVendorMethod); + wpa_printf(MSG_DEBUG, + "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u", + sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod, + sm->respVendor, sm->respVendorMethod); } @@ -1170,6 +1735,13 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) return DECISION_CONTINUE; } + if (!sm->identity && eap_get_erp_send_reauth_start(sm) && + !sm->initiate_reauth_start_sent) { + wpa_printf(MSG_DEBUG, + "EAP: getDecision: send EAP-Initiate/Re-auth-Start"); + return DECISION_INITIATE_REAUTH_START; + } + if (sm->identity == NULL || sm->currentId == -1) { wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " "yet -> CONTINUE"); @@ -1214,7 +1786,7 @@ static void eap_user_free(struct eap_user *user) { if (user == NULL) return; - os_free(user->password); + bin_clear_free(user->password, user->password_len); user->password = NULL; os_free(user); } @@ -1278,6 +1850,13 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->fragment_size = conf->fragment_size; sm->pwd_group = conf->pwd_group; sm->pbc_in_m1 = conf->pbc_in_m1; + sm->server_id = conf->server_id; + sm->server_id_len = conf->server_id_len; + sm->erp = conf->erp; + +#ifdef CONFIG_TESTING_OPTIONS + sm->tls_test_flags = conf->tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); @@ -1300,7 +1879,8 @@ void eap_server_sm_deinit(struct eap_sm *sm) if (sm->m && sm->eap_method_priv) sm->m->reset(sm, sm->eap_method_priv); wpabuf_free(sm->eap_if.eapReqData); - os_free(sm->eap_if.eapKeyData); + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + os_free(sm->eap_if.eapSessionId); wpabuf_free(sm->lastReqData); wpabuf_free(sm->eap_if.eapRespData); os_free(sm->identity); @@ -1309,7 +1889,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) os_free(sm->eap_fast_a_id_info); wpabuf_free(sm->eap_if.aaaEapReqData); wpabuf_free(sm->eap_if.aaaEapRespData); - os_free(sm->eap_if.aaaEapKeyData); + bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen); eap_user_free(sm->user); wpabuf_free(sm->assoc_wps_ie); wpabuf_free(sm->assoc_p2p_ie); diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 469b9a0fa653..db9b6aa2db39 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -241,7 +241,7 @@ static void eap_aka_reset(struct eap_sm *sm, void *priv) os_free(data->next_reauth_id); wpabuf_free(data->id_msgs); os_free(data->network_name); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -336,7 +336,7 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data, else sha1_vector(1, &addr, &len, hash); - if (os_memcmp(hash, checkcode, hash_len) != 0) { + if (os_memcmp_const(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } @@ -377,7 +377,7 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } - buf = eap_sim_msg_finish(msg, NULL, NULL, 0); + buf = eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); if (eap_aka_add_id_msg(data, buf) < 0) { wpabuf_free(buf); return NULL; @@ -534,7 +534,7 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0); } @@ -581,7 +581,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0); } @@ -620,7 +620,7 @@ static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0); } @@ -963,7 +963,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, */ if (attr->res == NULL || attr->res_len < data->res_len || attr->res_len_bits != data->res_len * 8 || - os_memcmp(attr->res, data->res, data->res_len) != 0) { + os_memcmp_const(attr->res, data->res, data->res_len) != 0) { wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " "include valid AT_RES (attr len=%lu, res len=%lu " "bits, expected %lu bits)", @@ -1040,6 +1040,7 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm, data->auts_reported = 1; /* Remain in CHALLENGE state to re-try after resynchronization */ + eap_aka_fullauth(sm, data); } @@ -1293,6 +1294,28 @@ static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = data->eap_method; + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_aka_register(void) { struct eap_method *eap; @@ -1312,6 +1335,7 @@ int eap_server_aka_register(void) eap->getKey = eap_aka_getKey; eap->isSuccess = eap_aka_isSuccess; eap->get_emsk = eap_aka_get_emsk; + eap->getSessionId = eap_aka_get_session_id; ret = eap_server_method_register(eap); if (ret) @@ -1341,6 +1365,7 @@ int eap_server_aka_prime_register(void) eap->getKey = eap_aka_getKey; eap->isSuccess = eap_aka_isSuccess; eap->get_emsk = eap_aka_get_emsk; + eap->getSessionId = eap_aka_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c new file mode 100644 index 000000000000..966f511ddddc --- /dev/null +++ b/src/eap_server/eap_server_eke.c @@ -0,0 +1,793 @@ +/* + * hostapd / EAP-EKE (RFC 6124) server + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_eke_common.h" + + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 peerid_type; + u8 serverid_type; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + u8 key[EAP_EKE_MAX_KEY_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + int phase2; + u32 failure_code; +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case FAILURE_REPORT: + return "FAILURE_REPORT"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), + eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_fail(struct eap_eke_data *data, u32 code) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code); + data->failure_code = code; + eap_eke_state(data, FAILURE_REPORT); +} + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + size_t i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + data->serverid_type = EAP_EKE_ID_OPAQUE; + for (i = 0; i < sm->server_id_len; i++) { + if (sm->server_id[i] == '.' && + data->serverid_type == EAP_EKE_ID_OPAQUE) + data->serverid_type = EAP_EKE_ID_FQDN; + if (sm->server_id[i] == '@') + data->serverid_type = EAP_EKE_ID_NAI; + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_eke_reset(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->peerid); + wpabuf_free(data->msgs); + bin_clear_free(data, sizeof(*data)); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, + u8 id, size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_REQUEST, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int supported_proposal(const u8 *pos) +{ + if (pos[0] == EAP_EKE_DHGROUP_EKE_16 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_15 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA1 && + pos[3] == EAP_EKE_MAC_HMAC_SHA1) + return 1; + + return 0; +} + + +static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x", + data->failure_code); + + msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); + if (msg == NULL) { + eap_eke_state(data, FAILURE); + return NULL; + } + wpabuf_put_be32(msg, data->failure_code); + + return msg; +} + + +static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm, + struct eap_eke_data *data, + u8 id) +{ + struct wpabuf *msg; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity"); + + plen = 2 + 4 * 4 + 1 + sm->server_id_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 4); /* NumProposals */ + wpabuf_put_u8(msg, 0); /* Reserved */ + + /* Proposal - DH Group 16 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 15 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 14 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* + * Proposal - DH Group 14 with AES128-CBC and SHA1 + * (mandatory to implement algorithms) + */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */ + + /* Server IDType + Identity */ + wpabuf_put_u8(msg, data->serverid_type); + wpabuf_put_data(msg, sm->server_id, sm->server_id_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_dup(msg); + if (data->msgs == NULL) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + u8 pub[EAP_EKE_MAX_DH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit"); + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_derive_key(&data->sess, sm->user->password, + sm->user->password_len, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, data->key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len, + EAP_EKE_COMMIT); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + /* + * y_s = g ^ x_s (mod p) + * x_s = random number 2 .. p-1 + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + * DHComponent_S = Encr(key, y_s) + */ + + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_dhcomp(&data->sess, data->key, pub, + wpabuf_put(msg, data->sess.dhcomp_len)) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put_buf(data->msgs, msg); + + return msg; +} + + +static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + size_t plen, prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 *auth; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm"); + + plen = data->sess.pnonce_ps_len + data->sess.prf_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + os_memcpy(nonces, data->nonce_p, data->sess.nonce_len); + os_memcpy(nonces + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + prot_len = wpabuf_tailroom(msg); + if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len, + wpabuf_put(msg, 0), &prot_len) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put(msg, prot_len); + + if (eap_eke_derive_ka(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + auth = wpabuf_put(msg, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len); + + return msg; +} + + +static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_eke_data *data = priv; + + switch (data->state) { + case IDENTITY: + return eap_eke_build_identity(sm, data, id); + case COMMIT: + return eap_eke_build_commit(sm, data, id); + case CONFIRM: + return eap_eke_build_confirm(sm, data, id); + case FAILURE_REPORT: + return eap_eke_build_failure(data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_eke_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + size_t len; + const u8 *pos; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame"); + return TRUE; + } + + eke_exch = *pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch); + + if (data->state == IDENTITY && eke_exch == EAP_EKE_ID) + return FALSE; + + if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT) + return FALSE; + + if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM) + return FALSE; + + if (eke_exch == EAP_EKE_FAILURE) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d", + eke_exch, data->state); + + return TRUE; +} + + +static void eap_eke_process_identity(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + int i; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity"); + + if (data->state != IDENTITY) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + 2 + 4 + 1 > end) { + wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + if (*pos != 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)", + *pos); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos += 2; + + if (!supported_proposal(pos)) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) < + 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + pos += 4; + + data->peerid_type = *pos++; + os_free(data->peerid); + data->peerid = os_malloc(end - pos); + if (data->peerid == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + os_memcpy(data->peerid, pos, end - pos); + data->peerid_len = end - pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", + data->peerid, data->peerid_len); + + if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { + wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + for (i = 0; i < EAP_MAX_METHODS; i++) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_EKE) + break; + } + if (i == EAP_MAX_METHODS) { + wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (sm->user->password == NULL || sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, COMMIT); +} + + +static void eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end, *dhcomp, *pnonce; + size_t decrypt_len; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); + + if (data->state != COMMIT) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); + pnonce = pos; + pos += data->sess.pnonce_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + if (eap_eke_derive_ke_ki(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + decrypt_len = sizeof(data->nonce_p); + if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, + data->nonce_p, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, CONFIRM); +} + + +static void eap_eke_process_confirm(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + size_t decrypt_len; + u8 nonce[EAP_EKE_MAX_NONCE_LEN]; + u8 auth_p[EAP_EKE_MAX_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (data->state != CONFIRM) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + decrypt_len = sizeof(nonce); + if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, + nonce, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", + nonce, data->sess.nonce_len); + if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); + if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + os_memset(data->key, 0, sizeof(data->key)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); +} + + +static void eap_eke_process_failure(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + u32 code; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); + + if (payloadlen < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + eap_eke_state(data, FAILURE); + return; + } + + code = WPA_GET_BE32(payload); + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); + + eap_eke_state(data, FAILURE); +} + + +static void eap_eke_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + u8 eke_exch; + size_t len; + const u8 *pos, *end; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) + return; + + eke_exch = *pos; + end = pos + len; + pos++; + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); + + switch (eke_exch) { + case EAP_EKE_ID: + eap_eke_process_identity(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + eap_eke_process_commit(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_CONFIRM: + eap_eke_process_confirm(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_FAILURE: + eap_eke_process_failure(sm, data, respData, pos, end - pos); + break; + } +} + + +static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->reset = eap_eke_reset; + eap->buildReq = eap_eke_buildReq; + eap->check = eap_eke_check; + eap->process = eap_eke_process; + eap->isDone = eap_eke_isDone; + eap->getKey = eap_eke_getKey; + eap->isSuccess = eap_eke_isSuccess; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index fcb80dc756de..6745100d338c 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -161,8 +161,8 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, return 0; } - if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8, - pac_opaque, buf) < 0) { + if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), + (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt " "PAC-Opaque"); os_free(buf); @@ -186,8 +186,7 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, switch (*pos) { case PAC_OPAQUE_TYPE_PAD: - pos = end; - break; + goto done; case PAC_OPAQUE_TYPE_KEY: if (pos[1] != EAP_FAST_PAC_KEY_LEN) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " @@ -218,6 +217,7 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, pos += 2 + pos[1]; } +done: if (pac_key == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in " @@ -511,7 +511,7 @@ static void eap_fast_reset(struct eap_sm *sm, void *priv) os_free(data->key_block_p); wpabuf_free(data->pending_phase2_resp); os_free(data->identity); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -730,8 +730,8 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, os_free(pac_buf); return NULL; } - if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf, - pac_opaque) < 0) { + if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr), + pac_len / 8, pac_buf, pac_opaque) < 0) { os_free(pac_buf); os_free(pac_opaque); return NULL; @@ -819,6 +819,9 @@ static int eap_fast_encrypt_phase2(struct eap_sm *sm, encr = eap_server_tls_encrypt(sm, &data->ssl, plain); wpabuf_free(plain); + if (!encr) + return -1; + if (data->ssl.tls_out && piggyback) { wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data " "(len=%d) with last Phase 1 Message (len=%d " @@ -1016,7 +1019,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, if (m->check(sm, priv, &buf)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to " "ignore the packet"); - next_type = eap_fast_req_failure(sm, data); + eap_fast_req_failure(sm, data); return; } @@ -1123,7 +1126,8 @@ static void eap_fast_process_phase2_eap(struct eap_sm *sm, static int eap_fast_parse_tlvs(struct wpabuf *data, struct eap_fast_tlv_parse *tlv) { - int mandatory, tlv_type, len, res; + int mandatory, tlv_type, res; + size_t len; u8 *pos, *end; os_memset(tlv, 0, sizeof(*tlv)); @@ -1136,13 +1140,14 @@ static int eap_fast_parse_tlvs(struct wpabuf *data, pos += 2; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) { + if (len > (size_t) (end - pos)) { wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); return -1; } wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " - "TLV type %d length %d%s", - tlv_type, len, mandatory ? " (mandatory)" : ""); + "TLV type %d length %u%s", + tlv_type, (unsigned int) len, + mandatory ? " (mandatory)" : ""); res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); if (res == -2) @@ -1196,7 +1201,7 @@ static int eap_fast_validate_crypto_binding( return -1; } - if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 || + if (os_memcmp_const(data->crypto_binding_nonce, b->nonce, 31) != 0 || (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in " "Crypto-Binding"); @@ -1210,7 +1215,7 @@ static int eap_fast_validate_crypto_binding( (u8 *) b, bind_len); hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len, b->compound_mac); - if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) { + if (os_memcmp_const(cmac, b->compound_mac, sizeof(cmac)) != 0) { wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", b->compound_mac, sizeof(cmac)); @@ -1587,6 +1592,18 @@ static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_FAST, + len); +} + + int eap_server_fast_register(void) { struct eap_method *eap; @@ -1606,6 +1623,7 @@ int eap_server_fast_register(void) eap->getKey = eap_fast_getKey; eap->get_emsk = eap_fast_get_emsk; eap->isSuccess = eap_fast_isSuccess; + eap->getSessionId = eap_fast_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index 2853c486ab4c..50f15c31d0dc 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -24,10 +24,10 @@ struct eap_gpsk_data { size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; + u8 session_id[128]; + size_t id_len; u8 *id_peer; size_t id_peer_len; - u8 *id_server; - size_t id_server_len; #define MAX_NUM_CSUITES 2 struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; size_t csuite_count; @@ -71,11 +71,6 @@ static void * eap_gpsk_init(struct eap_sm *sm) return NULL; data->state = GPSK_1; - /* TODO: add support for configuring ID_Server */ - data->id_server = (u8 *) os_strdup("hostapd"); - if (data->id_server) - data->id_server_len = os_strlen((char *) data->id_server); - data->csuite_count = 0; if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, EAP_GPSK_CIPHER_AES)) { @@ -101,9 +96,8 @@ static void * eap_gpsk_init(struct eap_sm *sm) static void eap_gpsk_reset(struct eap_sm *sm, void *priv) { struct eap_gpsk_data *data = priv; - os_free(data->id_server); os_free(data->id_peer); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -123,7 +117,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", data->rand_server, EAP_GPSK_RAND_LEN); - len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 + + len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 + data->csuite_count * sizeof(struct eap_gpsk_csuite); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -135,8 +129,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, } wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); - wpabuf_put_be16(req, data->id_server_len); - wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(req, data->csuite_count * sizeof(struct eap_gpsk_csuite)); @@ -158,7 +152,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); miclen = eap_gpsk_mic_len(data->vendor, data->specifier); - len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len + + len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -174,8 +168,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); - wpabuf_put_be16(req, data->id_server_len); - wpabuf_put_data(req, data->id_server, data->id_server_len); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); csuite = wpabuf_put(req, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); @@ -301,8 +295,8 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (alen != data->id_server_len || - os_memcmp(pos, data->id_server, alen) != 0) { + if (alen != sm->server_id_len || + os_memcmp(pos, sm->server_id, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); @@ -416,7 +410,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, - data->id_server, data->id_server_len, + sm->server_id, sm->server_id_len, data->msk, data->emsk, data->sk, &data->sk_len, data->pk, &data->pk_len) < 0) { @@ -425,6 +419,21 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, return; } + if (eap_gpsk_derive_session_id(sm->user->password, + sm->user->password_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + sm->server_id, sm->server_id_len, + EAP_TYPE_GPSK, + data->session_id, &data->id_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); + eap_gpsk_state(data, FAILURE); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", + data->session_id, data->id_len); + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " @@ -441,7 +450,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (os_memcmp(mic, pos, miclen) != 0) { + if (os_memcmp_const(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); @@ -510,7 +519,7 @@ static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (os_memcmp(mic, pos, miclen) != 0) { + if (os_memcmp_const(mic, pos, miclen) != 0) { wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4"); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); @@ -601,6 +610,24 @@ static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(data->id_len); + if (sid == NULL) + return NULL; + os_memcpy(sid, data->session_id, data->id_len); + *len = data->id_len; + + return sid; +} + + int eap_server_gpsk_register(void) { struct eap_method *eap; @@ -620,6 +647,7 @@ int eap_server_gpsk_register(void) eap->getKey = eap_gpsk_getKey; eap->isSuccess = eap_gpsk_isSuccess; eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c index f423106bfc9c..98ac3c6ec495 100644 --- a/src/eap_server/eap_server_gtc.c +++ b/src/eap_server/eap_server_gtc.c @@ -175,7 +175,7 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, } if (rlen != sm->user->password_len || - os_memcmp(pos, sm->user->password, rlen) != 0) { + os_memcmp_const(pos, sm->user->password, rlen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure"); data->state = FAILURE; } else { diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c index 51dc4e8b4f57..45015336b907 100644 --- a/src/eap_server/eap_server_identity.c +++ b/src/eap_server/eap_server_identity.c @@ -102,6 +102,7 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, struct eap_identity_data *data = priv; const u8 *pos; size_t len; + char *buf; if (data->pick_up) { if (eap_identity_check(sm, data, respData)) { @@ -119,6 +120,12 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, return; /* Should not happen - frame already validated */ wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); + buf = os_malloc(len * 4 + 1); + if (buf) { + printf_encode(buf, len * 4 + 1, pos, len); + eap_log_msg(sm, "EAP-Response/Identity '%s'", buf); + os_free(buf); + } if (sm->identity) sm->update_user = TRUE; os_free(sm->identity); diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 42aaca2b4812..16e62764cc55 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -103,8 +103,11 @@ static void * eap_ikev2_init(struct eap_sm *sm) data->ikev2.proposal.encr = ENCR_AES_CBC; data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; - data->ikev2.IDi = (u8 *) os_strdup("hostapd"); - data->ikev2.IDi_len = 7; + data->ikev2.IDi = os_malloc(sm->server_id_len); + if (data->ikev2.IDi == NULL) + goto failed; + os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); + data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; data->ikev2.cb_ctx = sm; @@ -124,7 +127,7 @@ static void eap_ikev2_reset(struct eap_sm *sm, void *priv) wpabuf_free(data->in_buf); wpabuf_free(data->out_buf); ikev2_initiator_deinit(&data->ikev2); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -253,7 +256,8 @@ static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv, static int eap_ikev2_process_icv(struct eap_ikev2_data *data, const struct wpabuf *respData, - u8 flags, const u8 *pos, const u8 **end) + u8 flags, const u8 *pos, const u8 **end, + int frag_ack) { if (flags & IKEV2_FLAGS_ICV_INCLUDED) { int icv_len = eap_ikev2_validate_icv( @@ -263,7 +267,7 @@ static int eap_ikev2_process_icv(struct eap_ikev2_data *data, return -1; /* Hide Integrity Checksum Data from further processing */ *end -= icv_len; - } else if (data->keys_ready) { + } else if (data->keys_ready && !frag_ack) { wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " "included integrity checksum"); return -1; @@ -305,6 +309,12 @@ static int eap_ikev2_process_fragment(struct eap_ikev2_data *data, if (data->in_buf == NULL) { /* First fragment of the message */ + if (message_length > 50000) { + /* Limit maximum memory allocation */ + wpa_printf(MSG_DEBUG, + "EAP-IKEV2: Ignore too long message"); + return -1; + } data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " @@ -362,7 +372,9 @@ static void eap_ikev2_process(struct eap_sm *sm, void *priv, } else flags = *pos++; - if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) { + if (eap_ikev2_process_icv(data, respData, flags, pos, &end, + data->state == WAIT_FRAG_ACK && len == 0) < 0) + { eap_ikev2_state(data, FAIL); return; } @@ -505,6 +517,36 @@ static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *sid; + size_t sid_len; + size_t offset; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; + sid = os_malloc(sid_len); + if (sid) { + offset = 0; + sid[offset] = EAP_TYPE_IKEV2; + offset++; + os_memcpy(sid + offset, data->ikev2.i_nonce, + data->ikev2.i_nonce_len); + offset += data->ikev2.i_nonce_len; + os_memcpy(sid + offset, data->ikev2.r_nonce, + data->ikev2.r_nonce_len); + *len = sid_len; + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", + sid, sid_len); + } + + return sid; +} + + int eap_server_ikev2_register(void) { struct eap_method *eap; @@ -525,6 +567,7 @@ int eap_server_ikev2_register(void) eap->getKey = eap_ikev2_getKey; eap->isSuccess = eap_ikev2_isSuccess; eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c index 5a5e2907efd6..71e8d59e0396 100644 --- a/src/eap_server/eap_server_md5.c +++ b/src/eap_server/eap_server_md5.c @@ -126,7 +126,7 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, return; } - if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { + if (os_memcmp_const(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); data->state = SUCCESS; } else { diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c index 0209fad63947..9e9dc934eb77 100644 --- a/src/eap_server/eap_server_methods.c +++ b/src/eap_server/eap_server_methods.c @@ -153,7 +153,7 @@ void eap_server_unregister_methods(void) * eap_server_get_name - Get EAP method name for the given EAP type * @vendor: EAP Vendor-Id (0 = IETF) * @type: EAP method type - * Returns: EAP method name, e.g., TLS, or %NULL if not found + * Returns: EAP method name, e.g., TLS, or "unknown" if not found * * This function maps EAP type numbers into EAP type names based on the list of * EAP methods included in the build. @@ -167,5 +167,5 @@ const char * eap_server_get_name(int vendor, EapType type) if (m->vendor == vendor && m->method == type) return m->name; } - return NULL; + return "unknown"; } diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 8d3dd5233beb..05848d2eaac5 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -91,7 +91,7 @@ static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) return; os_free(data->peer_challenge); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -100,7 +100,6 @@ static struct wpabuf * eap_mschapv2_build_challenge( { struct wpabuf *req; struct eap_mschapv2_hdr *ms; - char *name = "hostapd"; /* TODO: make this configurable */ size_t ms_len; if (!data->auth_challenge_from_tls && @@ -111,7 +110,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( return NULL; } - ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name); + ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { @@ -133,7 +132,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( wpabuf_put(req, CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", data->auth_challenge, CHALLENGE_LEN); - wpabuf_put_data(req, name, os_strlen(name)); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -291,6 +290,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, const u8 *username, *user; size_t username_len, user_len; int res; + char *buf; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); @@ -330,6 +330,13 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); + buf = os_malloc(name_len * 4 + 1); + if (buf) { + printf_encode(buf, name_len * 4 + 1, name, name_len); + eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf); + os_free(buf); + } + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ @@ -386,7 +393,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, return; } - if (os_memcmp(nt_response, expected, 24) == 0) { + if (os_memcmp_const(nt_response, expected, 24) == 0) { const u8 *pw_hash; u8 pw_hash_buf[16], pw_hash_hash[16]; @@ -407,13 +414,16 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, } pw_hash = pw_hash_buf; } - generate_authenticator_response_pwhash( - pw_hash, peer_challenge, data->auth_challenge, - username, username_len, nt_response, - data->auth_response); - - hash_nt_password_hash(pw_hash, pw_hash_hash); - get_master_key(pw_hash_hash, nt_response, data->master_key); + if (generate_authenticator_response_pwhash( + pw_hash, peer_challenge, data->auth_challenge, + username, username_len, nt_response, + data->auth_response) < 0 || + hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 || + get_master_key(pw_hash_hash, nt_response, + data->master_key)) { + data->state = FAILURE; + return; + } data->master_key_valid = 1; wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", data->master_key, MSCHAPV2_KEY_LEN); diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 35a42ad10748..0e6b4a0698ed 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -36,6 +36,7 @@ struct eap_pax_data { u8 mk[EAP_PAX_MK_LEN]; u8 ck[EAP_PAX_CK_LEN]; u8 ick[EAP_PAX_ICK_LEN]; + u8 mid[EAP_PAX_MID_LEN]; int keys_set; char *cid; size_t cid_len; @@ -64,7 +65,7 @@ static void eap_pax_reset(struct eap_sm *sm, void *priv) { struct eap_pax_data *data = priv; os_free(data->cid); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -148,7 +149,6 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, (u8 *) data->cid, data->cid_len, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); - pos += EAP_PAX_MAC_LEN; /* Optional ADE could be added here, if needed */ @@ -268,7 +268,7 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, wpabuf_mhead(respData), wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); - if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -287,7 +287,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, struct eap_pax_hdr *resp; u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; const u8 *pos; - size_t len, left; + size_t len, left, cid_len; int i; if (data->state != PAX_STD_1) @@ -320,7 +320,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); return; } - data->cid_len = WPA_GET_BE16(pos); + cid_len = WPA_GET_BE16(pos); + if (cid_len > 1500) { + wpa_printf(MSG_INFO, "EAP-PAX: Too long CID"); + return; + } + data->cid_len = cid_len; os_free(data->cid); data->cid = os_malloc(data->cid_len); if (data->cid == NULL) { @@ -383,7 +388,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, if (eap_pax_initial_key_derivation(data->mac_id, data->ak, data->rand.e, data->mk, data->ck, - data->ick) < 0) { + data->ick, data->mid) < 0) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " "key derivation"); data->state = FAILURE; @@ -395,7 +400,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, data->rand.r.x, EAP_PAX_RAND_LEN, data->rand.r.y, EAP_PAX_RAND_LEN, (u8 *) data->cid, data->cid_len, mac); - if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { + if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " "PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", @@ -417,7 +422,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, wpabuf_head(respData), wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); - if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", icvbuf, EAP_PAX_ICV_LEN); @@ -537,6 +542,26 @@ static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(1 + EAP_PAX_MID_LEN); + if (sid == NULL) + return NULL; + + *len = 1 + EAP_PAX_MID_LEN; + sid[0] = EAP_TYPE_PAX; + os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN); + + return sid; +} + + int eap_server_pax_register(void) { struct eap_method *eap; @@ -556,6 +581,7 @@ int eap_server_pax_register(void) eap->getKey = eap_pax_getKey; eap->isSuccess = eap_pax_isSuccess; eap->get_emsk = eap_pax_get_emsk; + eap->getSessionId = eap_pax_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 68253c438355..faa0fd2f2387 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -22,7 +22,6 @@ /* 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 - * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt */ #define EAP_PEAP_VERSION 1 @@ -99,33 +98,6 @@ static void eap_peap_state(struct eap_peap_data *data, int state) } -static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) -{ - struct wpabuf *e; - struct eap_tlv_hdr *tlv; - - if (buf == NULL) - return NULL; - - /* Encapsulate EAP packet in EAP-Payload TLV */ - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); - e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); - if (e == NULL) { - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " - "for TLV encapsulation"); - wpabuf_free(buf); - return NULL; - } - tlv = wpabuf_put(e, sizeof(*tlv)); - tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | - EAP_TLV_EAP_PAYLOAD_TLV); - tlv->length = host_to_be16(wpabuf_len(buf)); - wpabuf_put_buf(e, buf); - wpabuf_free(buf); - return e; -} - - static void eap_peap_req_success(struct eap_sm *sm, struct eap_peap_data *data) { @@ -200,7 +172,7 @@ static void eap_peap_reset(struct eap_sm *sm, void *priv) wpabuf_free(data->pending_phase2_resp); os_free(data->phase2_key); wpabuf_free(data->soh_response); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -239,8 +211,6 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, return NULL; } buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); - if (data->peap_version >= 2 && buf) - buf = eap_peapv2_tlv_eap_payload(buf); if (buf == NULL) return NULL; @@ -374,12 +344,14 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, size_t mlen; mlen = 6; /* Result TLV */ - if (data->crypto_binding != NO_BINDING) + if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && + data->crypto_binding != NO_BINDING) { mlen += 60; /* Cryptobinding TLV */ #ifdef EAP_SERVER_TNC - if (data->soh_response) - mlen += wpabuf_len(data->soh_response); + if (data->soh_response) + mlen += wpabuf_len(data->soh_response); #endif /* EAP_SERVER_TNC */ + } buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, EAP_CODE_REQUEST, id); @@ -425,8 +397,6 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, len[1] = 1; tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; - if (data->peap_version >= 2) - tlv_type |= EAP_TLV_TYPE_MANDATORY; wpabuf_put_be16(buf, tlv_type); wpabuf_put_be16(buf, 56); @@ -505,8 +475,7 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) return eap_peap_build_start(sm, data, id); case PHASE1: case PHASE1_ID2: - if (data->peap_version < 2 && - tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " "starting Phase2"); eap_peap_state(data, PHASE2_START); @@ -626,7 +595,7 @@ static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, buf[60] = EAP_TYPE_PEAP; hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); - if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " "cryptobinding TLV"); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20); @@ -1079,47 +1048,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm, wpabuf_free(in_decrypted); in_decrypted = nbuf; - } else if (data->peap_version >= 2) { - struct eap_tlv_hdr *tlv; - struct wpabuf *nmsg; - - if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " - "EAP TLV"); - wpabuf_free(in_decrypted); - return; - } - tlv = wpabuf_mhead(in_decrypted); - if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) != - EAP_TLV_EAP_PAYLOAD_TLV) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); - wpabuf_free(in_decrypted); - return; - } - if (sizeof(*tlv) + be_to_host16(tlv->length) > - wpabuf_len(in_decrypted)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " - "length"); - wpabuf_free(in_decrypted); - return; - } - hdr = (struct eap_hdr *) (tlv + 1); - if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " - "EAP packet in EAP TLV"); - wpabuf_free(in_decrypted); - return; - } - - nmsg = wpabuf_alloc(be_to_host16(hdr->length)); - if (nmsg == NULL) { - wpabuf_free(in_decrypted); - return; - } - - wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); - wpabuf_free(in_decrypted); - in_decrypted = nmsg; } hdr = wpabuf_head(in_decrypted); @@ -1168,53 +1096,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm, } -static int eap_peapv2_start_phase2(struct eap_sm *sm, - struct eap_peap_data *data) -{ - struct wpabuf *buf, *buf2; - - wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 " - "payload in the same message"); - eap_peap_state(data, PHASE1_ID2); - if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY)) - return -1; - - /* TODO: which Id to use here? */ - buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6); - if (buf == NULL) - return -1; - - buf2 = eap_peapv2_tlv_eap_payload(buf); - if (buf2 == NULL) - return -1; - - wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2); - - buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - buf2); - wpabuf_free(buf2); - - if (buf == NULL) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 " - "data"); - return -1; - } - - wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request", - buf); - - /* Append TLS data into the pending buffer after the Server Finished */ - if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) { - wpabuf_free(buf); - return -1; - } - wpabuf_put_buf(data->ssl.tls_out, buf); - wpabuf_free(buf); - - return 0; -} - - static int eap_peap_process_version(struct eap_sm *sm, void *priv, int peer_version) { @@ -1249,14 +1130,6 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, eap_peap_state(data, FAILURE); break; } - - if (data->peap_version >= 2 && - tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - if (eap_peapv2_start_phase2(sm, data)) { - eap_peap_state(data, FAILURE); - break; - } - } break; case PHASE2_START: eap_peap_state(data, PHASE2_ID); @@ -1358,6 +1231,18 @@ static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP, + len); +} + + int eap_server_peap_register(void) { struct eap_method *eap; @@ -1376,6 +1261,7 @@ int eap_server_peap_register(void) eap->isDone = eap_peap_isDone; eap->getKey = eap_peap_getKey; eap->isSuccess = eap_peap_isSuccess; + eap->getSessionId = eap_peap_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 0cd979920f4a..12b5d25d67ff 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -22,8 +22,8 @@ struct eap_psk_data { enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; u8 rand_s[EAP_PSK_RAND_LEN]; u8 rand_p[EAP_PSK_RAND_LEN]; - u8 *id_p, *id_s; - size_t id_p_len, id_s_len; + u8 *id_p; + size_t id_p_len; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; @@ -38,8 +38,6 @@ static void * eap_psk_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = PSK_1; - data->id_s = (u8 *) "hostapd"; - data->id_s_len = 7; return data; } @@ -49,7 +47,7 @@ static void eap_psk_reset(struct eap_sm *sm, void *priv) { struct eap_psk_data *data = priv; os_free(data->id_p); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -70,7 +68,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, data->rand_s, EAP_PSK_RAND_LEN); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, - sizeof(*psk) + data->id_s_len, + sizeof(*psk) + sm->server_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " @@ -82,7 +80,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, psk = wpabuf_put(req, sizeof(*psk)); psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); - wpabuf_put_data(req, data->id_s, data->id_s_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -112,13 +110,13 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ - buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buflen = sm->server_id_len + EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) goto fail; - os_memcpy(buf, data->id_s, data->id_s_len); - os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(buf, sm->server_id, sm->server_id_len); + os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { os_free(buf); goto fail; @@ -296,7 +294,7 @@ static void eap_psk_process_2(struct eap_sm *sm, os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ - buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) { data->state = FAILURE; @@ -304,8 +302,8 @@ static void eap_psk_process_2(struct eap_sm *sm, } 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, sm->server_id, sm->server_id_len); + pos += sm->server_id_len; os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); pos += EAP_PSK_RAND_LEN; os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); @@ -316,7 +314,7 @@ static void eap_psk_process_2(struct eap_sm *sm, } os_free(buf); wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); - if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { + if (os_memcmp_const(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", mac, EAP_PSK_MAC_LEN); @@ -487,6 +485,28 @@ static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_PSK_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_PSK; + os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_psk_register(void) { struct eap_method *eap; @@ -506,6 +526,7 @@ int eap_server_psk_register(void) eap->getKey = eap_psk_getKey; eap->isSuccess = eap_psk_isSuccess; eap->get_emsk = eap_psk_get_emsk; + eap->getSessionId = eap_psk_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index b61061bce702..943af0d15078 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -45,6 +45,7 @@ struct eap_pwd_data { u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; + u8 session_id[1 + SHA256_MAC_LEN]; BN_CTX *bnctx; }; @@ -105,7 +106,7 @@ static void * eap_pwd_init(struct eap_sm *sm) if (data->password == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password " "fail"); - os_free(data->id_server); + bin_clear_free(data->id_server, data->id_server_len); os_free(data); return NULL; } @@ -115,15 +116,16 @@ static void * eap_pwd_init(struct eap_sm *sm) data->bnctx = BN_CTX_new(); if (data->bnctx == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); - os_free(data->password); - os_free(data->id_server); + bin_clear_free(data->password, data->password_len); + bin_clear_free(data->id_server, data->id_server_len); os_free(data); return NULL; } data->in_frag_pos = data->out_frag_pos = 0; data->inbuf = data->outbuf = NULL; - data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + /* use default MTU from RFC 5931 if not configured otherwise */ + data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020; return data; } @@ -133,24 +135,26 @@ static void eap_pwd_reset(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; - BN_free(data->private_value); - BN_free(data->peer_scalar); - BN_free(data->my_scalar); - BN_free(data->k); + BN_clear_free(data->private_value); + BN_clear_free(data->peer_scalar); + BN_clear_free(data->my_scalar); + BN_clear_free(data->k); BN_CTX_free(data->bnctx); - EC_POINT_free(data->my_element); - EC_POINT_free(data->peer_element); - os_free(data->id_peer); - os_free(data->id_server); - os_free(data->password); + EC_POINT_clear_free(data->my_element); + EC_POINT_clear_free(data->peer_element); + bin_clear_free(data->id_peer, data->id_peer_len); + bin_clear_free(data->id_server, data->id_server_len); + bin_clear_free(data->password, data->password_len); if (data->grp) { EC_GROUP_free(data->grp->group); - EC_POINT_free(data->grp->pwe); - BN_free(data->grp->order); - BN_free(data->grp->prime); + EC_POINT_clear_free(data->grp->pwe); + BN_clear_free(data->grp->order); + BN_clear_free(data->grp->prime); os_free(data->grp); } - os_free(data); + wpabuf_free(data->inbuf); + wpabuf_free(data->outbuf); + bin_clear_free(data, sizeof(*data)); } @@ -206,11 +210,15 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - BN_rand_range(data->private_value, data->grp->order); - BN_rand_range(mask, data->grp->order); - BN_add(data->my_scalar, data->private_value, mask); - BN_mod(data->my_scalar, data->my_scalar, data->grp->order, - data->bnctx); + if (BN_rand_range(data->private_value, data->grp->order) != 1 || + BN_rand_range(mask, data->grp->order) != 1 || + BN_add(data->my_scalar, data->private_value, mask) != 1 || + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx) != 1) { + wpa_printf(MSG_INFO, + "EAP-pwd (server): unable to get randomness"); + goto fin; + } if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, data->grp->pwe, mask, data->bnctx)) { @@ -226,7 +234,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, "fail"); goto fin; } - BN_free(mask); + BN_clear_free(mask); if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { @@ -278,8 +286,8 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, fin: os_free(scalar); os_free(element); - BN_free(x); - BN_free(y); + BN_clear_free(x); + BN_clear_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -402,9 +410,9 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - os_free(cruft); - BN_free(x); - BN_free(y); + bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); + BN_clear_free(x); + BN_clear_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -523,6 +531,7 @@ eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) */ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { wpabuf_free(data->outbuf); + data->outbuf = NULL; data->out_frag_pos = 0; } @@ -595,7 +604,8 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", data->id_peer, data->id_peer_len); - if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) { + data->grp = os_zalloc(sizeof(EAP_PWD_group)); + if (data->grp == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); return; @@ -718,11 +728,11 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, res = 1; fin: - EC_POINT_free(K); - EC_POINT_free(point); - BN_free(cofactor); - BN_free(x); - BN_free(y); + EC_POINT_clear_free(K); + EC_POINT_clear_free(point); + BN_clear_free(cofactor); + BN_clear_free(x); + BN_clear_free(y); if (res) eap_pwd_state(data, PWD_Confirm_Req); @@ -829,7 +839,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, eap_pwd_h_final(hash, conf); ptr = (u8 *) payload; - if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { + if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " "verify"); goto fin; @@ -838,15 +848,16 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); if (compute_keys(data->grp, data->bnctx, data->k, data->peer_scalar, data->my_scalar, conf, - data->my_confirm, &cs, data->msk, data->emsk) < 0) + data->my_confirm, &cs, data->msk, data->emsk, + data->session_id) < 0) eap_pwd_state(data, FAILURE); else eap_pwd_state(data, SUCCESS); fin: - os_free(cruft); - BN_free(x); - BN_free(y); + bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); + BN_clear_free(x); + BN_clear_free(y); } @@ -893,6 +904,8 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, tot_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " "length = %d", tot_len); + if (tot_len > 15000) + return; data->inbuf = wpabuf_alloc(tot_len); if (data->inbuf == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " @@ -949,6 +962,7 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, */ if (data->in_frag_pos) { wpabuf_free(data->inbuf); + data->inbuf = NULL; data->in_frag_pos = 0; } } @@ -1006,6 +1020,25 @@ static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv) } +static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + id = os_malloc(1 + SHA256_MAC_LEN); + if (id == NULL) + return NULL; + + os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); + *len = 1 + SHA256_MAC_LEN; + + return id; +} + + int eap_server_pwd_register(void) { struct eap_method *eap; @@ -1014,8 +1047,6 @@ int eap_server_pwd_register(void) struct timezone tz; u32 sr; - EVP_add_digest(EVP_sha256()); - sr = 0xdeaddada; (void) gettimeofday(&tp, &tz); sr ^= (tp.tv_sec ^ tp.tv_usec); @@ -1036,6 +1067,7 @@ int eap_server_pwd_register(void) eap->getKey = eap_pwd_getkey; eap->get_emsk = eap_pwd_get_emsk; eap->isSuccess = eap_pwd_is_success; + eap->getSessionId = eap_pwd_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index f72e1bf510c6..de7077731899 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -27,8 +27,6 @@ struct eap_sake_data { u8 session_id; u8 *peerid; size_t peerid_len; - u8 *serverid; - size_t serverid_len; }; @@ -77,11 +75,6 @@ static void * eap_sake_init(struct eap_sm *sm) wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d", data->session_id); - /* TODO: add support for configuring SERVERID */ - data->serverid = (u8 *) os_strdup("hostapd"); - if (data->serverid) - data->serverid_len = os_strlen((char *) data->serverid); - return data; } @@ -89,9 +82,8 @@ static void * eap_sake_init(struct eap_sm *sm) static void eap_sake_reset(struct eap_sm *sm, void *priv) { struct eap_sake_data *data = priv; - os_free(data->serverid); os_free(data->peerid); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -131,8 +123,7 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); plen = 4; - if (data->serverid) - plen += 2 + data->serverid_len; + plen += 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); if (msg == NULL) { data->state = FAILURE; @@ -142,11 +133,9 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ"); eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2); - if (data->serverid) { - wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, - data->serverid, data->serverid_len); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -169,9 +158,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", data->rand_s, EAP_SAKE_RAND_LEN); - plen = 2 + EAP_SAKE_RAND_LEN; - if (data->serverid) - plen += 2 + data->serverid_len; + plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE); if (msg == NULL) { data->state = FAILURE; @@ -182,11 +169,9 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S, data->rand_s, EAP_SAKE_RAND_LEN); - if (data->serverid) { - wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); - eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, - data->serverid, data->serverid_len); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -213,7 +198,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN); mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN); if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 0, wpabuf_head(msg), wpabuf_len(msg), mic, mic)) { @@ -362,11 +347,11 @@ static void eap_sake_process_challenge(struct eap_sm *sm, (u8 *) &data->tek, data->msk, data->emsk); eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); - if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { + if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); return; @@ -399,11 +384,11 @@ static void eap_sake_process_confirm(struct eap_sm *sm, } eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); - if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { + if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); eap_sake_state(data, FAILURE); } else @@ -510,6 +495,28 @@ static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_SAKE_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SAKE; + os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_sake_register(void) { struct eap_method *eap; @@ -529,6 +536,7 @@ int eap_server_sake_register(void) eap->getKey = eap_sake_getKey; eap->isSuccess = eap_sake_isSuccess; eap->get_emsk = eap_sake_get_emsk; + eap->getSessionId = eap_sake_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index b531241e84e1..ddfb71cf4e2e 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -94,7 +94,7 @@ static void eap_sim_reset(struct eap_sm *sm, void *priv) struct eap_sim_data *data = priv; os_free(data->next_pseudonym); os_free(data->next_reauth_id); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -140,7 +140,7 @@ static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, ver[1] = EAP_SIM_VERSION; eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), ver, sizeof(ver)); - return eap_sim_msg_finish(msg, NULL, NULL, 0); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); } @@ -240,8 +240,8 @@ static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt, - EAP_SIM_NONCE_MT_LEN); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); } @@ -278,7 +278,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0); } @@ -317,7 +317,7 @@ static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm, wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); } - return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); + return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0); } @@ -820,6 +820,29 @@ static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SIM; + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_sim_register(void) { struct eap_method *eap; @@ -839,6 +862,7 @@ int eap_server_sim_register(void) eap->getKey = eap_sim_getKey; eap->isSuccess = eap_sim_isSuccess; eap->get_emsk = eap_sim_get_emsk; + eap->getSessionId = eap_sim_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 447f47cfa00a..58cfe8ac64a0 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -94,6 +94,28 @@ static void * eap_unauth_tls_init(struct eap_sm *sm) #endif /* EAP_SERVER_UNAUTH_TLS */ +#ifdef CONFIG_HS20 +static void * eap_wfa_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE; + return data; +} +#endif /* CONFIG_HS20 */ + + static void eap_tls_reset(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -178,6 +200,10 @@ static Boolean eap_tls_check(struct eap_sm *sm, void *priv, pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, respData, &len); + else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, respData, + &len); else pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, respData, &len); @@ -261,7 +287,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (emsk) os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); - os_free(eapKeyData); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); } else emsk = NULL; @@ -284,6 +310,18 @@ static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS, + len); +} + + int eap_server_tls_register(void) { struct eap_method *eap; @@ -303,6 +341,7 @@ int eap_server_tls_register(void) eap->getKey = eap_tls_getKey; eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; + eap->getSessionId = eap_tls_get_session_id; ret = eap_server_method_register(eap); if (ret) @@ -340,3 +379,34 @@ int eap_server_unauth_tls_register(void) return ret; } #endif /* EAP_SERVER_UNAUTH_TLS */ + + +#ifdef CONFIG_HS20 +int eap_server_wfa_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, + "WFA-UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_wfa_unauth_tls_init; + eap->reset = eap_tls_reset; + eap->buildReq = eap_tls_buildReq; + eap->check = eap_tls_check; + eap->process = eap_tls_process; + eap->isDone = eap_tls_isDone; + eap->getKey = eap_tls_getKey; + eap->isSuccess = eap_tls_isSuccess; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} +#endif /* CONFIG_HS20 */ diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 9efb5b293853..56916c45ac69 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -25,14 +25,32 @@ struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, code, identifier); + else if (type == EAP_WFA_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, payload_len, + code, identifier); return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, identifier); } +#ifdef CONFIG_TLS_INTERNAL +static void eap_server_tls_log_cb(void *ctx, const char *msg) +{ + struct eap_sm *sm = ctx; + eap_log_msg(sm, "TLS: %s", msg); +} +#endif /* CONFIG_TLS_INTERNAL */ + + int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); + return -1; + } + data->eap = sm; data->phase2 = sm->init_phase2; @@ -43,6 +61,13 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } +#ifdef CONFIG_TLS_INTERNAL + tls_connection_set_log_cb(data->conn, eap_server_tls_log_cb, sm); +#ifdef CONFIG_TESTING_OPTIONS + tls_connection_set_test_flags(data->conn, sm->tls_test_flags); +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_TLS_INTERNAL */ + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); @@ -115,6 +140,47 @@ fail: } +/** + * eap_server_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + if (tls_connection_get_keys(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; +} + + struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, int eap_type, int version, u8 id) { @@ -388,6 +454,10 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, EAP_VENDOR_TYPE_UNAUTH_TLS, respData, &left); + else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW, + EAP_VENDOR_WFA_UNAUTH_TLS, respData, + &left); else pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c index 67a3dfa30611..21bd26f8296e 100644 --- a/src/eap_server/eap_server_tnc.c +++ b/src/eap_server/eap_server_tnc.c @@ -480,7 +480,8 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, message_length = WPA_GET_BE32(pos); pos += 4; - if (message_length < (u32) (end - pos)) { + if (message_length < (u32) (end - pos) || + message_length > 75000) { wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 647bd2fad938..12a31b07a63b 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -336,7 +336,7 @@ static void eap_ttls_reset(struct eap_sm *sm, void *priv) data->phase2_method->reset(sm, data->phase2_priv); eap_server_tls_ssl_deinit(sm, &data->ssl); wpabuf_free(data->pending_phase2_eap_resp); - os_free(data); + bin_clear_free(data, sizeof(*data)); } @@ -409,7 +409,7 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( RADIUS_VENDOR_ID_MICROSOFT, 1, 43); *pos++ = data->mschapv2_ident; ret = os_snprintf((char *) pos, end - pos, "S="); - if (ret >= 0 && ret < end - pos) + if (!os_snprintf_error(end - pos, ret)) pos += ret; pos += wpa_snprintf_hex_uppercase( (char *) pos, end - pos, data->mschapv2_auth_response, @@ -509,8 +509,8 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } if (sm->user->password_len != user_password_len || - os_memcmp(sm->user->password, user_password, user_password_len) != - 0) { + os_memcmp_const(sm->user->password, user_password, + user_password_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); eap_ttls_state(data, FAILURE); return; @@ -558,7 +558,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, return; } - if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || + if (os_memcmp_const(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) + != 0 || password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); os_free(chal); @@ -571,7 +572,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, chap_md5(password[0], sm->user->password, sm->user->password_len, challenge, challenge_len, hash); - if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { + if (os_memcmp_const(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == + 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); eap_ttls_state(data, SUCCESS); } else { @@ -616,7 +618,8 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } - if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || + if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) + != 0 || response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); os_free(chal); @@ -631,7 +634,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, nt_challenge_response(challenge, sm->user->password, sm->user->password_len, nt_response); - if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) { + if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); eap_ttls_state(data, SUCCESS); } else { @@ -703,7 +706,8 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, return; } - if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || + if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) + != 0 || response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); os_free(chal); @@ -736,7 +740,7 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; - if (os_memcmp(nt_response, rx_resp, 24) == 0) { + if (os_memcmp_const(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; @@ -984,6 +988,16 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } if (parse.user_name) { + char *nbuf; + nbuf = os_malloc(parse.user_name_len * 4 + 1); + if (nbuf) { + printf_encode(nbuf, parse.user_name_len * 4 + 1, + parse.user_name, + parse.user_name_len); + eap_log_msg(sm, "TTLS-User-Name '%s'", nbuf); + os_free(nbuf); + } + os_free(sm->identity); sm->identity = os_malloc(parse.user_name_len); if (sm->identity == NULL) { @@ -1167,6 +1181,50 @@ static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS, + len); +} + + +static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = os_malloc(EAP_EMSK_LEN); + if (emsk) + os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + } else + emsk = NULL; + + if (emsk) { + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive EMSK"); + } + + return emsk; +} + + int eap_server_ttls_register(void) { struct eap_method *eap; @@ -1185,6 +1243,8 @@ int eap_server_ttls_register(void) eap->isDone = eap_ttls_isDone; eap->getKey = eap_ttls_getKey; eap->isSuccess = eap_ttls_isSuccess; + eap->getSessionId = eap_ttls_get_session_id; + eap->get_emsk = eap_ttls_get_emsk; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 97ec0c0eaa99..9d9c28d704c1 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -380,7 +380,7 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv, message_length = WPA_GET_BE16(pos); pos += 2; - if (message_length < end - pos) { + if (message_length < end - pos || message_length > 50000) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); return; diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index 257013e3e661..acf5435300dc 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -38,7 +38,6 @@ struct eap_sim_db_pending { char imsi[20]; enum { PENDING, SUCCESS, FAILURE } state; void *cb_session_ctx; - struct os_time timestamp; int aka; union { struct { @@ -574,16 +573,14 @@ static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) char buf[1000], *pos, *cmd, *imsi; int res; - res = recv(sock, buf, sizeof(buf), 0); + res = recv(sock, buf, sizeof(buf) - 1, 0); if (res < 0) return; + buf[res] = '\0'; wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " "external source", (u8 *) buf, res); if (res == 0) return; - if (res >= (int) sizeof(buf)) - res = sizeof(buf) - 1; - buf[res] = '\0'; if (data->get_complete_cb == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " @@ -630,7 +627,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (data->sock < 0) { - perror("socket(eap_sim_db)"); + wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno)); return -1; } @@ -640,8 +637,13 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) "/tmp/eap_sim_db_%d-%d", getpid(), counter++); os_free(data->local_sock); data->local_sock = os_strdup(addr.sun_path); + if (data->local_sock == NULL) { + close(data->sock); + data->sock = -1; + return -1; + } if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(eap_sim_db)"); + wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno)); close(data->sock); data->sock = -1; return -1; @@ -651,12 +653,16 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("connect(eap_sim_db)"); + wpa_printf(MSG_INFO, "connect(eap_sim_db): %s", + strerror(errno)); wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", (u8 *) addr.sun_path, os_strlen(addr.sun_path)); close(data->sock); data->sock = -1; + unlink(data->local_sock); + os_free(data->local_sock); + data->local_sock = NULL; return -1; } @@ -804,7 +810,8 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, if (send(data->sock, msg, len, 0) < 0) { _errno = errno; - perror("send[EAP-SIM DB UNIX]"); + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); } if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || @@ -816,7 +823,8 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " "external server"); if (send(data->sock, msg, len, 0) < 0) { - perror("send[EAP-SIM DB UNIX]"); + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); return -1; } } @@ -914,12 +922,13 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); - if (len < 0 || len + imsi_len >= sizeof(msg)) + if (os_snprintf_error(sizeof(msg), len) || + len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); - if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + if (os_snprintf_error(sizeof(msg) - len, ret)) return EAP_SIM_DB_FAILURE; len += ret; @@ -932,7 +941,6 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, if (entry == NULL) return EAP_SIM_DB_FAILURE; - os_get_time(&entry->timestamp); os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; @@ -957,7 +965,7 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) pos = id; end = id + sizeof(buf) * 2 + 2; *pos++ = prefix; - pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); + wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); return id; } @@ -1378,7 +1386,8 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); - if (len < 0 || len + imsi_len >= sizeof(msg)) + if (os_snprintf_error(sizeof(msg), len) || + len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; @@ -1392,7 +1401,6 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, if (entry == NULL) return EAP_SIM_DB_FAILURE; - os_get_time(&entry->timestamp); entry->aka = 1; os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; @@ -1443,19 +1451,20 @@ int eap_sim_db_resynchronize(struct eap_sim_db_data *data, imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); - if (len < 0 || len + imsi_len >= sizeof(msg)) + if (os_snprintf_error(sizeof(msg), len) || + len + imsi_len >= sizeof(msg)) return -1; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " "); - if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + if (os_snprintf_error(sizeof(msg) - len, ret)) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, auts, EAP_AKA_AUTS_LEN); ret = os_snprintf(msg + len, sizeof(msg) - len, " "); - if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + if (os_snprintf_error(sizeof(msg) - len, ret)) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, @@ -1480,7 +1489,6 @@ int eap_sim_db_resynchronize(struct eap_sim_db_data *data, */ char * sim_get_username(const u8 *identity, size_t identity_len) { - char *username; size_t pos; if (identity == NULL) @@ -1491,11 +1499,5 @@ char * sim_get_username(const u8 *identity, size_t identity_len) break; } - username = os_malloc(pos + 1); - if (username == NULL) - return NULL; - os_memcpy(username, identity, pos); - username[pos] = '\0'; - - return username; + return dup_binstr(identity, pos); } diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 11f5827513ab..ddf90b859ee4 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -64,6 +64,7 @@ struct eap_ssl_data { /* dummy type used as a flag for UNAUTH-TLS */ #define EAP_UNAUTH_TLS_TYPE 255 +#define EAP_WFA_UNAUTH_TLS_TYPE 254 struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, @@ -73,6 +74,9 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); +u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, int eap_type, int version, u8 id); struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version); diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c index 0e77efb7a2ef..632598fac72a 100644 --- a/src/eap_server/ikev2.c +++ b/src/eap_server/ikev2.c @@ -633,7 +633,7 @@ static int ikev2_process_auth_secret(struct ikev2_initiator_data *data, return -1; if (auth_len != prf->hash_len || - os_memcmp(auth, auth_data, auth_len) != 0) { + os_memcmp_const(auth, auth_data, auth_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", auth, auth_len); @@ -990,7 +990,7 @@ static int ikev2_build_kei(struct ikev2_initiator_data *data, */ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); wpabuf_put_buf(msg, pv); - os_free(pv); + wpabuf_free(pv); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c index 5e332ae02088..dc6f689c0b5e 100644 --- a/src/eap_server/tncs.c +++ b/src/eap_server/tncs.c @@ -11,6 +11,7 @@ #include "common.h" #include "base64.h" +#include "common/tnc.h" #include "tncs.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_defs.h" @@ -19,7 +20,9 @@ /* TODO: TNCS must be thread-safe; review the code and add locking etc. if * needed.. */ +#ifndef TNC_CONFIG_FILE #define TNC_CONFIG_FILE "/etc/tnc_config" +#endif /* TNC_CONFIG_FILE */ #define IF_TNCCS_START \ "\n" \ ""); if (start == NULL || end == NULL || start > end) { @@ -1183,6 +1115,9 @@ int tncs_global_init(void) { struct tnc_if_imv *imv; + if (tncs_global_data) + return 0; + tncs_global_data = os_zalloc(sizeof(*tncs_global_data)); if (tncs_global_data == NULL) return -1; diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/eapol_auth/Makefile +++ b/src/eapol_auth/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c index b6e0b137a52f..557958286d06 100644 --- a/src/eapol_auth/eapol_auth_dump.c +++ b/src/eapol_auth/eapol_auth_dump.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - State dump - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -118,108 +118,172 @@ static inline const char * ctrl_dir_state_txt(int s) } -void eapol_auth_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm) +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen) { - fprintf(f, "%sEAPOL state machine:\n", prefix); - fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, - sm->aWhile, sm->quietWhile, sm->reAuthWhen); + char *pos, *end; + int ret; + + pos = buf; + end = pos + buflen; + + ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n" + "reAuthWhen=%d\n", + sm->aWhile, sm->quietWhile, sm->reAuthWhen); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + #define _SB(b) ((b) ? "TRUE" : "FALSE") - fprintf(f, - "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" - "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" - "%s eapSuccess=%s eapTimeout=%s initialize=%s " - "keyAvailable=%s\n" - "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" - "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", - prefix, _SB(sm->authAbort), _SB(sm->authFail), - port_state_txt(sm->authPortStatus), _SB(sm->authStart), - prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), - _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), - prefix, _SB(sm->eap_if->eapSuccess), - _SB(sm->eap_if->eapTimeout), - _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), - prefix, _SB(sm->keyDone), _SB(sm->keyRun), - _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), - prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), - _SB(sm->reAuthenticate)); - - fprintf(f, "%s Authenticator PAE:\n" - "%s state=%s\n" - "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" - "%s portMode=%s reAuthCount=%d\n" - "%s quietPeriod=%d reAuthMax=%d\n" - "%s authEntersConnecting=%d\n" - "%s authEapLogoffsWhileConnecting=%d\n" - "%s authEntersAuthenticating=%d\n" - "%s authAuthSuccessesWhileAuthenticating=%d\n" - "%s authAuthTimeoutsWhileAuthenticating=%d\n" - "%s authAuthFailWhileAuthenticating=%d\n" - "%s authAuthEapStartsWhileAuthenticating=%d\n" - "%s authAuthEapLogoffWhileAuthenticating=%d\n" - "%s authAuthReauthsWhileAuthenticated=%d\n" - "%s authAuthEapStartsWhileAuthenticated=%d\n" - "%s authAuthEapLogoffWhileAuthenticated=%d\n", - prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, - _SB(sm->eapolLogoff), _SB(sm->eapolStart), - _SB(sm->eap_if->eapRestart), - prefix, port_type_txt(sm->portMode), sm->reAuthCount, - prefix, sm->quietPeriod, sm->reAuthMax, - prefix, sm->authEntersConnecting, - prefix, sm->authEapLogoffsWhileConnecting, - prefix, sm->authEntersAuthenticating, - prefix, sm->authAuthSuccessesWhileAuthenticating, - prefix, sm->authAuthTimeoutsWhileAuthenticating, - prefix, sm->authAuthFailWhileAuthenticating, - prefix, sm->authAuthEapStartsWhileAuthenticating, - prefix, sm->authAuthEapLogoffWhileAuthenticating, - prefix, sm->authAuthReauthsWhileAuthenticated, - prefix, sm->authAuthEapStartsWhileAuthenticated, - prefix, sm->authAuthEapLogoffWhileAuthenticated); - - fprintf(f, "%s Backend Authentication:\n" - "%s state=%s\n" - "%s eapNoReq=%s eapReq=%s eapResp=%s\n" - "%s serverTimeout=%d\n" - "%s backendResponses=%d\n" - "%s backendAccessChallenges=%d\n" - "%s backendOtherRequestsToSupplicant=%d\n" - "%s backendAuthSuccesses=%d\n" - "%s backendAuthFails=%d\n", - prefix, prefix, - be_auth_state_txt(sm->be_auth_state), - prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), - _SB(sm->eap_if->eapResp), - prefix, sm->serverTimeout, - prefix, sm->backendResponses, - prefix, sm->backendAccessChallenges, - prefix, sm->backendOtherRequestsToSupplicant, - prefix, sm->backendAuthSuccesses, - prefix, sm->backendAuthFails); - - fprintf(f, "%s Reauthentication Timer:\n" - "%s state=%s\n" - "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, - reauth_timer_state_txt(sm->reauth_timer_state), prefix, - sm->reAuthPeriod, _SB(sm->reAuthEnabled)); - - fprintf(f, "%s Authenticator Key Transmit:\n" - "%s state=%s\n", prefix, prefix, - auth_key_tx_state_txt(sm->auth_key_tx_state)); - - fprintf(f, "%s Key Receive:\n" - "%s state=%s\n" - "%s rxKey=%s\n", prefix, prefix, - key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); - - fprintf(f, "%s Controlled Directions:\n" - "%s state=%s\n" - "%s adminControlledDirections=%s " - "operControlledDirections=%s\n" - "%s operEdge=%s\n", prefix, prefix, - ctrl_dir_state_txt(sm->ctrl_dir_state), - prefix, ctrl_dir_txt(sm->adminControlledDirections), - ctrl_dir_txt(sm->operControlledDirections), - prefix, _SB(sm->operEdge)); + ret = os_snprintf(pos, end - pos, + "authAbort=%s\n" + "authFail=%s\n" + "authPortStatus=%s\n" + "authStart=%s\n" + "authTimeout=%s\n" + "authSuccess=%s\n" + "eapFail=%s\n" + "eapolEap=%s\n" + "eapSuccess=%s\n" + "eapTimeout=%s\n" + "initialize=%s\n" + "keyAvailable=%s\n" + "keyDone=%s\n" + "keyRun=%s\n" + "keyTxEnabled=%s\n" + "portControl=%s\n" + "portEnabled=%s\n" + "portValid=%s\n" + "reAuthenticate=%s\n", + _SB(sm->authAbort), + _SB(sm->authFail), + port_state_txt(sm->authPortStatus), + _SB(sm->authStart), + _SB(sm->authTimeout), + _SB(sm->authSuccess), + _SB(sm->eap_if->eapFail), + _SB(sm->eapolEap), + _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), + _SB(sm->eap_if->eapKeyAvailable), + _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), + port_type_txt(sm->portControl), + _SB(sm->eap_if->portEnabled), + _SB(sm->portValid), + _SB(sm->reAuthenticate)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "auth_pae_state=%s\n" + "eapolLogoff=%s\n" + "eapolStart=%s\n" + "eapRestart=%s\n" + "portMode=%s\n" + "reAuthCount=%d\n" + "quietPeriod=%d\n" + "reAuthMax=%d\n" + "authEntersConnecting=%d\n" + "authEapLogoffsWhileConnecting=%d\n" + "authEntersAuthenticating=%d\n" + "authAuthSuccessesWhileAuthenticating=%d\n" + "authAuthTimeoutsWhileAuthenticating=%d\n" + "authAuthFailWhileAuthenticating=%d\n" + "authAuthEapStartsWhileAuthenticating=%d\n" + "authAuthEapLogoffWhileAuthenticating=%d\n" + "authAuthReauthsWhileAuthenticated=%d\n" + "authAuthEapStartsWhileAuthenticated=%d\n" + "authAuthEapLogoffWhileAuthenticated=%d\n", + auth_pae_state_txt(sm->auth_pae_state), + _SB(sm->eapolLogoff), + _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), + port_type_txt(sm->portMode), + sm->reAuthCount, + sm->quietPeriod, sm->reAuthMax, + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "be_auth_state=%s\n" + "eapNoReq=%s\n" + "eapReq=%s\n" + "eapResp=%s\n" + "serverTimeout=%d\n" + "backendResponses=%d\n" + "backendAccessChallenges=%d\n" + "backendOtherRequestsToSupplicant=%d\n" + "backendAuthSuccesses=%d\n" + "backendAuthFails=%d\n", + be_auth_state_txt(sm->be_auth_state), + _SB(sm->eap_if->eapNoReq), + _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), + sm->serverTimeout, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "reauth_timer_state=%s\n" + "reAuthPeriod=%d\n" + "reAuthEnabled=%s\n", + reauth_timer_state_txt(sm->reauth_timer_state), + sm->reAuthPeriod, + _SB(sm->reAuthEnabled)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "auth_key_tx_state=%s\n", + auth_key_tx_state_txt(sm->auth_key_tx_state)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "key_rx_state=%s\n" + "rxKey=%s\n", + key_rx_state_txt(sm->key_rx_state), + _SB(sm->rxKey)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "ctrl_dir_state=%s\n" + "adminControlledDirections=%s\n" + "operControlledDirections=%s\n" + "operEdge=%s\n", + ctrl_dir_state_txt(sm->ctrl_dir_state), + ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + _SB(sm->operEdge)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; #undef _SB + + return pos - buf; } diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index c3ccb46bf383..0df6eb56416b 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -43,6 +43,7 @@ sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); static void eapol_auth_initialize(struct eapol_state_machine *sm); +static void eapol_auth_conf_free(struct eapol_auth_config *conf); static void eapol_auth_logger(struct eapol_authenticator *eapol, @@ -219,7 +220,8 @@ SM_STATE(AUTH_PAE, DISCONNECTED) sm->eapolLogoff = FALSE; if (!from_initialize) { sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, - sm->flags & EAPOL_SM_PREAUTH); + sm->flags & EAPOL_SM_PREAUTH, + sm->remediation); } } @@ -276,7 +278,7 @@ SM_STATE(AUTH_PAE, HELD) eap_server_get_name(0, sm->eap_type_supp)); } sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, - sm->flags & EAPOL_SM_PREAUTH); + sm->flags & EAPOL_SM_PREAUTH, sm->remediation); } @@ -302,7 +304,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) eap_server_get_name(0, sm->eap_type_authsrv), extra); sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, - sm->flags & EAPOL_SM_PREAUTH); + sm->flags & EAPOL_SM_PREAUTH, sm->remediation); } @@ -830,6 +832,9 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.fragment_size = eapol->conf.fragment_size; eap_conf.pwd_group = eapol->conf.pwd_group; eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; + eap_conf.server_id = eapol->conf.server_id; + eap_conf.server_id_len = eapol->conf.server_id_len; + eap_conf.erp = eapol->conf.erp; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -848,6 +853,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, sm->radius_cui = wpabuf_alloc_copy(radius_cui, os_strlen(radius_cui)); + sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++; + if (eapol->acct_multi_session_id_lo == 0) + eapol->acct_multi_session_id_hi++; + sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; + return sm; } @@ -999,8 +1009,13 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, struct eap_user *user) { struct eapol_state_machine *sm = ctx; - return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, - identity_len, phase2, user); + int ret; + + ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, + identity_len, phase2, user); + if (user->remediation) + sm->remediation = 1; + return ret; } @@ -1012,10 +1027,44 @@ static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) } +static int eapol_sm_get_erp_send_reauth_start(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_send_reauth_start; +} + + +static const char * eapol_sm_get_erp_domain(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_domain; +} + + +static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx, + const char *keyname) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname); +} + + +static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp); +} + + static struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, - eapol_sm_get_eap_req_id_text + eapol_sm_get_eap_req_id_text, + NULL, + eapol_sm_get_erp_send_reauth_start, + eapol_sm_get_erp_domain, + eapol_sm_erp_get_key, + eapol_sm_erp_add_key, }; @@ -1045,6 +1094,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, os_free(dst->eap_req_id_text); dst->pwd_group = src->pwd_group; dst->pbc_in_m1 = src->pbc_in_m1; + dst->server_id = src->server_id; + dst->server_id_len = src->server_id_len; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) @@ -1058,16 +1109,16 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, } if (src->pac_opaque_encr_key) { dst->pac_opaque_encr_key = os_malloc(16); + if (dst->pac_opaque_encr_key == NULL) + goto fail; os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 16); } else dst->pac_opaque_encr_key = NULL; if (src->eap_fast_a_id) { dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); - if (dst->eap_fast_a_id == NULL) { - os_free(dst->eap_req_id_text); - return -1; - } + if (dst->eap_fast_a_id == NULL) + goto fail; os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, src->eap_fast_a_id_len); dst->eap_fast_a_id_len = src->eap_fast_a_id_len; @@ -1075,11 +1126,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_fast_a_id = NULL; if (src->eap_fast_a_id_info) { dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); - if (dst->eap_fast_a_id_info == NULL) { - os_free(dst->eap_req_id_text); - os_free(dst->eap_fast_a_id); - return -1; - } + if (dst->eap_fast_a_id_info == NULL) + goto fail; } else dst->eap_fast_a_id_info = NULL; dst->eap_fast_prov = src->eap_fast_prov; @@ -1089,7 +1137,23 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->tnc = src->tnc; dst->wps = src->wps; dst->fragment_size = src->fragment_size; + + os_free(dst->erp_domain); + if (src->erp_domain) { + dst->erp_domain = os_strdup(src->erp_domain); + if (dst->erp_domain == NULL) + goto fail; + } else { + dst->erp_domain = NULL; + } + dst->erp_send_reauth_start = src->erp_send_reauth_start; + dst->erp = src->erp; + return 0; + +fail: + eapol_auth_conf_free(dst); + return -1; } @@ -1103,6 +1167,8 @@ static void eapol_auth_conf_free(struct eapol_auth_config *conf) conf->eap_fast_a_id = NULL; os_free(conf->eap_fast_a_id_info); conf->eap_fast_a_id_info = NULL; + os_free(conf->erp_domain); + conf->erp_domain = NULL; } @@ -1110,6 +1176,7 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, struct eapol_auth_cb *cb) { struct eapol_authenticator *eapol; + struct os_time now; eapol = os_zalloc(sizeof(*eapol)); if (eapol == NULL) @@ -1135,6 +1202,14 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, eapol->cb.abort_auth = cb->abort_auth; eapol->cb.tx_key = cb->tx_key; eapol->cb.eapol_event = cb->eapol_event; + eapol->cb.erp_get_key = cb->erp_get_key; + eapol->cb.erp_add_key = cb->erp_add_key; + + /* Acct-Multi-Session-Id should be unique over reboots. If reliable + * clock is not available, this could be replaced with reboot counter, + * etc. */ + os_get_time(&now); + eapol->acct_multi_session_id_hi = now.sec; return eapol; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index b50bbdd0f1ce..ebed19adefc7 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -24,6 +24,9 @@ struct eapol_auth_config { void *eap_sim_db_priv; char *eap_req_id_text; /* a copy of this will be allocated */ size_t eap_req_id_text_len; + int erp_send_reauth_start; + char *erp_domain; /* a copy of this will be allocated */ + int erp; /* Whether ERP is enabled on authentication server */ u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -37,12 +40,15 @@ struct eapol_auth_config { int fragment_size; u16 pwd_group; int pbc_in_m1; + const u8 *server_id; + size_t server_id_len; /* Opaque context pointer to owner data for callback functions */ void *ctx; }; struct eap_user; +struct eap_server_erp_key; typedef enum { EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING @@ -58,7 +64,8 @@ struct eapol_auth_cb { size_t datalen); void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, size_t datalen); - void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); + void (*finished)(void *ctx, void *sta_ctx, int success, int preauth, + int remediation); int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); int (*sta_entry_alive)(void *ctx, const u8 *addr); @@ -68,6 +75,9 @@ struct eapol_auth_cb { void (*abort_auth)(void *ctx, void *sta_ctx); void (*tx_key)(void *ctx, void *sta_ctx); void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type); + struct eap_server_erp_key * (*erp_get_key)(void *ctx, + const char *keyname); + int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp); }; @@ -81,8 +91,8 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, const char *identity, const char *radius_cui); void eapol_auth_free(struct eapol_state_machine *sm); void eapol_auth_step(struct eapol_state_machine *sm); -void eapol_auth_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm); +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen); int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); #endif /* EAPOL_AUTH_SM_H */ diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h index d7f893a1d666..a29b49c90c72 100644 --- a/src/eapol_auth/eapol_auth_sm_i.h +++ b/src/eapol_auth/eapol_auth_sm_i.h @@ -30,6 +30,9 @@ struct eapol_authenticator { u8 *default_wep_key; u8 default_wep_key_idx; + + u32 acct_multi_session_id_hi; + u32 acct_multi_session_id_lo; }; @@ -173,6 +176,11 @@ struct eapol_state_machine { struct eapol_authenticator *eapol; void *sta; /* station context pointer to use in callbacks */ + + int remediation; + + u32 acct_multi_session_id_hi; + u32 acct_multi_session_id_lo; }; #endif /* EAPOL_AUTH_SM_I_H */ diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/eapol_supp/Makefile +++ b/src/eapol_supp/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index f90fb6257880..9cc234a82b09 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -16,6 +16,7 @@ #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 @@ -127,6 +128,7 @@ struct eapol_sm { 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; @@ -136,6 +138,13 @@ struct eapol_sm { 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 */ }; @@ -205,7 +214,6 @@ SM_STATE(SUPP_PAE, LOGOFF) SM_ENTRY(SUPP_PAE, LOGOFF); eapol_sm_txLogoff(sm); sm->logoffSent = TRUE; - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); } @@ -215,8 +223,8 @@ SM_STATE(SUPP_PAE, DISCONNECTED) SM_ENTRY(SUPP_PAE, DISCONNECTED); sm->sPortMode = Auto; sm->startCount = 0; + sm->eapTriggerStart = FALSE; sm->logoffSent = FALSE; - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); sm->suppAbort = TRUE; @@ -238,6 +246,11 @@ SM_STATE(SUPP_PAE, CONNECTING) { int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; SM_ENTRY(SUPP_PAE, CONNECTING); + + if (sm->eapTriggerStart) + send_start = 1; + sm->eapTriggerStart = FALSE; + if (send_start) { sm->startWhen = sm->startPeriod; sm->startCount++; @@ -249,12 +262,14 @@ SM_STATE(SUPP_PAE, CONNECTING) * delay authentication. Use a short timeout to send the first * EAPOL-Start if Authenticator does not start authentication. */ -#ifdef CONFIG_WPS - /* Reduce latency on starting WPS negotiation. */ - sm->startWhen = 1; -#else /* CONFIG_WPS */ - sm->startWhen = 3; -#endif /* CONFIG_WPS */ + 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; @@ -281,7 +296,6 @@ SM_STATE(SUPP_PAE, HELD) SM_ENTRY(SUPP_PAE, HELD); sm->heldWhile = sm->heldPeriod; eapol_enable_timer_tick(sm); - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); sm->cb_status = EAPOL_CB_FAILURE; } @@ -290,7 +304,6 @@ SM_STATE(SUPP_PAE, HELD) SM_STATE(SUPP_PAE, AUTHENTICATED) { SM_ENTRY(SUPP_PAE, AUTHENTICATED); - sm->suppPortStatus = Authorized; eapol_sm_set_port_authorized(sm); sm->cb_status = EAPOL_CB_SUCCESS; } @@ -306,7 +319,6 @@ SM_STATE(SUPP_PAE, RESTART) SM_STATE(SUPP_PAE, S_FORCE_AUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); - sm->suppPortStatus = Authorized; eapol_sm_set_port_authorized(sm); sm->sPortMode = ForceAuthorized; } @@ -315,7 +327,6 @@ SM_STATE(SUPP_PAE, S_FORCE_AUTH) SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); sm->sPortMode = ForceUnauthorized; eapol_sm_txLogoff(sm); @@ -382,6 +393,8 @@ SM_STEP(SUPP_PAE) 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) @@ -463,6 +476,17 @@ SM_STATE(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 */ @@ -706,8 +730,8 @@ static void eapol_sm_processKey(struct eapol_sm *sm) 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(orig_key_sign, key->key_signature, - IEEE8021X_KEY_SIGN_LEN) != 0) { + 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, @@ -806,6 +830,19 @@ 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 " @@ -850,14 +887,24 @@ static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) static void eapol_sm_set_port_authorized(struct eapol_sm *sm) { - if (sm->ctx->port_cb) + 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) { - if (sm->ctx->port_cb) + 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); } @@ -883,6 +930,13 @@ void eapol_sm_step(struct eapol_sm *sm) 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) @@ -897,9 +951,15 @@ void eapol_sm_step(struct eapol_sm *sm) } if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { - int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; + 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, success, sm->ctx->cb_ctx); + sm->ctx->cb(sm, result, sm->ctx->cb_ctx); } } @@ -1048,7 +1108,7 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, "suppPortStatus=%s\n", eapol_supp_pae_state(sm->SUPP_PAE_state), eapol_port_status(sm->suppPortStatus)); - if (len < 0 || (size_t) len >= buflen) + if (os_snprintf_error(buflen, len)) return 0; if (verbose) { @@ -1065,11 +1125,18 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, sm->maxStart, eapol_port_control(sm->portControl), eapol_supp_be_state(sm->SUPP_BE_state)); - if (ret < 0 || (size_t) ret >= buflen - len) + 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; @@ -1112,7 +1179,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) "Authorized" : "Unauthorized", sm->SUPP_BE_state); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; len = ret; @@ -1140,7 +1207,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) sm->dot1xSuppLastEapolFrameVersion, MAC2STR(sm->dot1xSuppLastEapolFrameSource)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1186,7 +1253,7 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, return 0; } #ifdef CONFIG_WPS - if (sm->conf.workaround && + 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)) { @@ -1214,6 +1281,24 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, 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 @@ -1227,6 +1312,16 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, 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; @@ -1261,6 +1356,13 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, 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); @@ -1299,6 +1401,8 @@ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) 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); } @@ -1387,6 +1491,9 @@ void eapol_sm_notify_config(struct eapol_sm *sm, 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; @@ -1395,10 +1502,18 @@ void eapol_sm_notify_config(struct eapol_sm *sm, 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); } } @@ -1419,6 +1534,22 @@ 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; @@ -1428,6 +1559,9 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) 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)", @@ -1441,6 +1575,24 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) } +/** + * 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() @@ -1452,6 +1604,10 @@ 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); } } @@ -1478,21 +1634,15 @@ void eapol_sm_notify_cached(struct eapol_sm *sm) /** * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() - * @attempt: Whether PMKSA caching is tried * - * Notify EAPOL state machines whether PMKSA caching is used. + * Notify EAPOL state machines if PMKSA caching is used. */ -void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm) { if (sm == NULL) return; - if (attempt) { - wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); - sm->cached_pmk = TRUE; - } else { - wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); - sm->cached_pmk = FALSE; - } + wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); + sm->cached_pmk = TRUE; } @@ -1504,7 +1654,6 @@ static void eapol_sm_abort_cached(struct eapol_sm *sm) return; sm->cached_pmk = FALSE; sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; - sm->suppPortStatus = Unauthorized; eapol_sm_set_port_unauthorized(sm); /* Make sure we do not start sending EAPOL-Start frames first, but @@ -1676,6 +1825,8 @@ static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) return sm->altAccept; case EAPOL_altReject: return sm->altReject; + case EAPOL_eapTriggerStart: + return sm->eapTriggerStart; } return FALSE; } @@ -1715,6 +1866,9 @@ static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, case EAPOL_altReject: sm->altReject = value; break; + case EAPOL_eapTriggerStart: + sm->eapTriggerStart = value; + break; } } @@ -1802,13 +1956,14 @@ static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, - const char *cert_hash, + 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, - cert_hash, cert); + sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject, + num_altsubject, cert_hash, cert); } @@ -1822,6 +1977,17 @@ static void eapol_sm_notify_status(void *ctx, const char *status, } +#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; @@ -1845,6 +2011,9 @@ static struct eapol_callbacks eapol_cb = 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 }; @@ -1880,6 +2049,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) 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; @@ -1889,7 +2059,16 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) 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; @@ -1915,6 +2094,9 @@ void eapol_sm_deinit(struct eapol_sm *sm) 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); @@ -1936,3 +2118,22 @@ int eapol_sm_failed(struct eapol_sm *sm) 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/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index c4b87da1f6bf..1309ff7547e8 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -53,11 +53,29 @@ struct eapol_config { * 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 */ @@ -78,7 +96,7 @@ struct eapol_ctx { /** * cb - Function to be called when EAPOL negotiation has been completed * @eapol: Pointer to EAPOL state machine data - * @success: Whether the authentication was completed successfully + * @result: Whether the authentication was completed successfully * @ctx: Pointer to context data (cb_ctx) * * This optional callback function will be called when the EAPOL @@ -86,7 +104,8 @@ struct eapol_ctx { * 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, int success, void *ctx); + void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result, + void *ctx); /** * cb_ctx - Callback context for cb() @@ -192,6 +211,15 @@ struct eapol_ctx { */ 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 * @@ -220,10 +248,13 @@ struct eapol_ctx { * @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); /** @@ -240,6 +271,14 @@ struct eapol_ctx { 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 @@ -273,9 +312,10 @@ 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, int attempt); +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); @@ -287,6 +327,8 @@ 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) { @@ -346,13 +388,20 @@ 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) { } -#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0) +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) @@ -386,6 +435,9 @@ 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/src/l2_packet/Makefile b/src/l2_packet/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/l2_packet/Makefile +++ b/src/l2_packet/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index dd825b56894d..2a452458214b 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -39,6 +39,11 @@ struct l2_ethhdr { #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 @@ -62,6 +67,19 @@ struct l2_packet_data * l2_packet_init( 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() @@ -121,4 +139,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); */ 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/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c index 2e9a04c8943c..aa836482767b 100644 --- a/src/l2_packet/l2_packet_freebsd.c +++ b/src/l2_packet/l2_packet_freebsd.c @@ -256,6 +256,18 @@ struct l2_packet_data * l2_packet_init( } +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) { @@ -308,3 +320,10 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) 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/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index 1419830db7ac..41de2f85d9cd 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Layer2 packet handling with Linux packet sockets - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,9 +10,12 @@ #include #include #include +#include #include "common.h" #include "eloop.h" +#include "crypto/sha1.h" +#include "crypto/crypto.h" #include "l2_packet.h" @@ -26,6 +29,56 @@ struct l2_packet_data { void *rx_callback_ctx; int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ + + /* For working around Linux packet socket behavior and regression. */ + int fd_br_rx; + int last_from_br; + u8 last_hash[SHA1_MAC_LEN]; + unsigned int num_rx, num_rx_br; +}; + +/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and + * src port bootps and dst port bootpc' + */ +static struct sock_filter dhcp_sock_filter_insns[] = { + { 0x80, 0, 0, 0x00000000 }, + { 0x35, 0, 12, 0x00000116 }, + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 10, 0x00000800 }, + { 0x30, 0, 0, 0x00000017 }, + { 0x15, 0, 8, 0x00000011 }, + { 0x28, 0, 0, 0x00000014 }, + { 0x45, 6, 0, 0x00001fff }, + { 0xb1, 0, 0, 0x0000000e }, + { 0x48, 0, 0, 0x0000000e }, + { 0x15, 0, 3, 0x00000043 }, + { 0x48, 0, 0, 0x00000010 }, + { 0x15, 0, 1, 0x00000044 }, + { 0x6, 0, 0, 0x00000bb8 }, + { 0x6, 0, 0, 0x00000000 }, +}; + +static const struct sock_fprog dhcp_sock_filter = { + .len = ARRAY_SIZE(dhcp_sock_filter_insns), + .filter = dhcp_sock_filter_insns, +}; + + +/* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */ +static struct sock_filter ndisc_sock_filter_insns[] = { + { 0x30, 0, 0, 0x00000000 }, + { 0x45, 0, 5, 0x00000001 }, + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 3, 0x000086dd }, + { 0x30, 0, 0, 0x00000014 }, + { 0x15, 0, 1, 0x0000003a }, + { 0x6, 0, 0, 0x000005dc }, + { 0x6, 0, 0, 0x00000000 }, +}; + +static const struct sock_fprog ndisc_sock_filter = { + .len = ARRAY_SIZE(ndisc_sock_filter_insns), + .filter = ndisc_sock_filter_insns, }; @@ -74,6 +127,7 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) struct sockaddr_ll ll; socklen_t fromlen; + l2->num_rx++; os_memset(&ll, 0, sizeof(ll)); fromlen = sizeof(ll); res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, @@ -84,6 +138,80 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) return; } + wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d", + __func__, MAC2STR(ll.sll_addr), (int) res); + + if (l2->fd_br_rx >= 0) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[1]; + size_t len[1]; + + /* + * Close the workaround socket if the kernel version seems to be + * able to deliver packets through the packet socket before + * authorization has been completed (in dormant state). + */ + if (l2->num_rx_br <= 1) { + wpa_printf(MSG_DEBUG, + "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket", + l2->ifname); + eloop_unregister_read_sock(l2->fd_br_rx); + close(l2->fd_br_rx); + l2->fd_br_rx = -1; + } + + addr[0] = buf; + len[0] = res; + sha1_vector(1, addr, len, hash); + if (l2->last_from_br && + os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", + __func__); + return; + } + os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN); + } + + l2->last_from_br = 0; + l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); +} + + +static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_ll ll; + socklen_t fromlen; + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[1]; + size_t len[1]; + + l2->num_rx_br++; + os_memset(&ll, 0, sizeof(ll)); + fromlen = sizeof(ll); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, + &fromlen); + if (res < 0) { + wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s", + strerror(errno)); + return; + } + + wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d", + __func__, MAC2STR(ll.sll_addr), (int) res); + + addr[0] = buf; + len[0] = res; + sha1_vector(1, addr, len, hash); + if (!l2->last_from_br && + os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__); + return; + } + l2->last_from_br = 1; + os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN); l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); } @@ -105,6 +233,7 @@ struct l2_packet_data * l2_packet_init( l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; + l2->fd_br_rx = -1; l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); @@ -152,6 +281,87 @@ struct l2_packet_data * l2_packet_init( } +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) +{ + struct l2_packet_data *l2; + struct sock_filter ethertype_sock_filter_insns[] = { + /* Load ethertype */ + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN), + /* Jump over next statement if ethertype does not match */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1), + /* Ethertype match - return all */ + BPF_STMT(BPF_RET | BPF_K, ~0), + /* No match - drop */ + BPF_STMT(BPF_RET | BPF_K, 0) + }; + const struct sock_fprog ethertype_sock_filter = { + .len = ARRAY_SIZE(ethertype_sock_filter_insns), + .filter = ethertype_sock_filter_insns, + }; + struct sockaddr_ll ll; + + l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); + if (!l2) + return NULL; + + /* + * The Linux packet socket behavior has changed over the years and there + * is an inconvenient regression in it that breaks RX for a specific + * protocol from interfaces in a bridge when that interface is not in + * fully operation state (i.e., when in station mode and not completed + * authorization). To work around this, register ETH_P_ALL version of + * the packet socket bound to the real netdev and use socket filter to + * match the ethertype value. This version is less efficient, but + * required for functionality with many kernel version. If the main + * packet socket is found to be working, this less efficient version + * gets closed automatically. + */ + + l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, + htons(ETH_P_ALL)); + if (l2->fd_br_rx < 0) { + wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s", + __func__, strerror(errno)); + /* try to continue without the workaround RX socket */ + return l2; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = if_nametoindex(ifname); + ll.sll_protocol = htons(ETH_P_ALL); + if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s", + __func__, strerror(errno)); + /* try to continue without the workaround RX socket */ + close(l2->fd_br_rx); + l2->fd_br_rx = -1; + return l2; + } + + if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER, + ðertype_sock_filter, sizeof(struct sock_fprog))) { + wpa_printf(MSG_DEBUG, + "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + /* try to continue without the workaround RX socket */ + close(l2->fd_br_rx); + l2->fd_br_rx = -1; + return l2; + } + + eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL); + + return l2; +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) @@ -161,7 +371,12 @@ void l2_packet_deinit(struct l2_packet_data *l2) eloop_unregister_read_sock(l2->fd); close(l2->fd); } - + + if (l2->fd_br_rx >= 0) { + eloop_unregister_read_sock(l2->fd_br_rx); + close(l2->fd_br_rx); + } + os_free(l2); } @@ -202,3 +417,31 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) 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) +{ + const struct sock_fprog *sock_filter; + + switch (type) { + case L2_PACKET_FILTER_DHCP: + sock_filter = &dhcp_sock_filter; + break; + case L2_PACKET_FILTER_NDISC: + sock_filter = &ndisc_sock_filter; + break; + default: + return -1; + } + + if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER, + sock_filter, sizeof(struct sock_fprog))) { + wpa_printf(MSG_ERROR, + "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c index 23b8ddcc9e36..716778164af4 100644 --- a/src/l2_packet/l2_packet_ndis.c +++ b/src/l2_packet/l2_packet_ndis.c @@ -450,6 +450,18 @@ struct l2_packet_data * l2_packet_init( } +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) @@ -514,3 +526,10 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) 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/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c index b01e830220f8..307fc6daa403 100644 --- a/src/l2_packet/l2_packet_none.c +++ b/src/l2_packet/l2_packet_none.c @@ -84,12 +84,25 @@ struct l2_packet_data * l2_packet_init( * TODO: open connection for receiving frames */ l2->fd = -1; - eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + if (l2->fd >= 0) + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, 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) @@ -115,3 +128,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2) { /* This function can be left empty */ } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c index 45aef56bcdbd..bb4f4a31d015 100644 --- a/src/l2_packet/l2_packet_pcap.c +++ b/src/l2_packet/l2_packet_pcap.c @@ -54,15 +54,16 @@ static int l2_packet_init_libdnet(struct l2_packet_data *l2) l2->eth = eth_open(l2->ifname); if (!l2->eth) { - printf("Failed to open interface '%s'.\n", l2->ifname); - perror("eth_open"); + wpa_printf(MSG_ERROR, + "Failed to open interface '%s' - eth_open: %s", + l2->ifname, strerror(errno)); return -1; } if (eth_get(l2->eth, &own_addr) < 0) { - printf("Failed to get own hw address from interface '%s'.\n", - l2->ifname); - perror("eth_get"); + wpa_printf(MSG_ERROR, + "Failed to get own hw address from interface '%s' - eth_get: %s", + l2->ifname, strerror(errno)); eth_close(l2->eth); l2->eth = NULL; return -1; @@ -378,3 +379,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2) l2, l2->pcap); #endif /* CONFIG_WINPCAP */ } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c index 6b117ca2b907..e26ca20a8625 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -44,7 +44,7 @@ static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, msg.msg_namelen = sizeof(l2->priv_addr); if (sendmsg(l2->fd, &msg, 0) < 0) { - perror("L2: sendmsg(cmd)"); + wpa_printf(MSG_ERROR, "L2: sendmsg(cmd): %s", strerror(errno)); return -1; } @@ -82,7 +82,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, msg.msg_namelen = sizeof(l2->priv_addr); if (sendmsg(l2->fd, &msg, 0) < 0) { - perror("L2: sendmsg(packet_send)"); + wpa_printf(MSG_ERROR, "L2: sendmsg(packet_send): %s", + strerror(errno)); return -1; } @@ -102,7 +103,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("l2_packet_receive - recvfrom"); + wpa_printf(MSG_ERROR, "l2_packet_receive - recvfrom: %s", + strerror(errno)); return; } if (res < ETH_ALEN) { @@ -162,7 +164,7 @@ struct l2_packet_data * l2_packet_init( l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); if (l2->fd < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); os_free(l2->own_socket_path); l2->own_socket_path = NULL; os_free(l2); @@ -173,7 +175,8 @@ struct l2_packet_data * l2_packet_init( addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("l2-pkt-privsep: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "l2-pkt-privsep: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } @@ -191,14 +194,14 @@ struct l2_packet_data * l2_packet_init( tv.tv_usec = 0; res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { - perror("select"); + wpa_printf(MSG_ERROR, "select: %s", strerror(errno)); goto fail; } if (FD_ISSET(l2->fd, &rfds)) { res = recv(l2->fd, reply, sizeof(reply), 0); if (res < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); goto fail; } } else { @@ -228,6 +231,18 @@ fail: } +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) @@ -259,3 +274,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2) { wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c index b6e5088938ec..74085a3169af 100644 --- a/src/l2_packet/l2_packet_winpcap.c +++ b/src/l2_packet/l2_packet_winpcap.c @@ -248,6 +248,18 @@ struct l2_packet_data * l2_packet_init( } +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); +} + + static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx) { struct l2_packet_data *l2 = eloop_ctx; diff --git a/src/lib.rules b/src/lib.rules index b260d25a050c..0c79d992a6aa 100644 --- a/src/lib.rules +++ b/src/lib.rules @@ -15,6 +15,10 @@ ifeq ($(V), 1) Q= E=true endif +ifeq ($(QUIET), 1) +Q=@ +E=true +endif %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< diff --git a/src/p2p/Makefile b/src/p2p/Makefile index cffba620da04..adfd3dfd5b9b 100644 --- a/src/p2p/Makefile +++ b/src/p2p/Makefile @@ -2,8 +2,7 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index b994a44abc1c..6adb3dc2049f 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -13,6 +13,8 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" @@ -42,21 +44,32 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer * entries will be removed */ -#define P2P_PEER_EXPIRATION_AGE 300 +#ifndef P2P_PEER_EXPIRATION_AGE +#define P2P_PEER_EXPIRATION_AGE 60 +#endif /* P2P_PEER_EXPIRATION_AGE */ #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) static void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; - struct os_time now; + struct os_reltime now; size_t i; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) continue; + if (dev == p2p->go_neg_peer) { + /* + * GO Negotiation is in progress with the peer, so + * don't expire the peer entry until GO Negotiation + * fails or times out. + */ + continue; + } + if (p2p->cfg->go_connected && p2p->cfg->go_connected(p2p->cfg->cb_ctx, dev->info.p2p_device_addr)) { @@ -64,7 +77,7 @@ static void p2p_expire_peers(struct p2p_data *p2p) * We are connected as a client to a group in which the * peer is the GO, so do not expire the peer entry. */ - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); continue; } @@ -78,12 +91,12 @@ static void p2p_expire_peers(struct p2p_data *p2p) * The peer is connected as a client in a group where * we are the GO, so do not expire the peer entry. */ - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); continue; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer " - "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); + p2p_dbg(p2p, "Expiring old peer entry " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); dl_list_del(&dev->list); p2p_device_free(p2p, dev); } @@ -128,16 +141,31 @@ static const char * p2p_state_txt(int state) return "INVITE"; case P2P_INVITE_LISTEN: return "INVITE_LISTEN"; - case P2P_SEARCH_WHEN_READY: - return "SEARCH_WHEN_READY"; - case P2P_CONTINUE_SEARCH_WHEN_READY: - return "CONTINUE_SEARCH_WHEN_READY"; default: return "?"; } } +const char * p2p_get_state_txt(struct p2p_data *p2p) +{ + return p2p_state_txt(p2p->state); +} + + +struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p) +{ + return p2p ? p2p->p2ps_adv_list : NULL; +} + + +void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr) +{ + if (p2p && intended_addr) + os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN); +} + + u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) { struct p2p_device *dev = NULL; @@ -168,16 +196,23 @@ void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) void p2p_set_state(struct p2p_data *p2p, int new_state) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s", + p2p_dbg(p2p, "State %s -> %s", p2p_state_txt(p2p->state), p2p_state_txt(new_state)); p2p->state = new_state; + + if (new_state == P2P_IDLE && p2p->pending_channel) { + p2p_dbg(p2p, "Apply change in listen channel"); + p2p->cfg->reg_class = p2p->pending_reg_class; + p2p->cfg->channel = p2p->pending_channel; + p2p->pending_reg_class = 0; + p2p->pending_channel = 0; + } } void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Set timeout (state=%s): %u.%06u sec", + p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec", p2p_state_txt(p2p->state), sec, usec); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); @@ -186,30 +221,40 @@ void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) void p2p_clear_timeout(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state)); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); } -void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, - int status) +void p2p_go_neg_failed(struct p2p_data *p2p, int status) { struct p2p_go_neg_results res; - p2p_clear_timeout(p2p); - p2p_set_state(p2p, P2P_IDLE); - if (p2p->go_neg_peer) - p2p->go_neg_peer->wps_method = WPS_NOT_READY; + struct p2p_device *peer = p2p->go_neg_peer; + + if (!peer) + return; + + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); + if (p2p->state != P2P_SEARCH) { + /* + * Clear timeouts related to GO Negotiation if no new p2p_find + * has been started. + */ + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + } + + peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; + peer->wps_method = WPS_NOT_READY; + peer->oob_pw_id = 0; + wpabuf_free(peer->go_neg_conf); + peer->go_neg_conf = NULL; p2p->go_neg_peer = NULL; os_memset(&res, 0, sizeof(res)); res.status = status; - if (peer) { - os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, - ETH_ALEN); - os_memcpy(res.peer_interface_addr, peer->intended_addr, - ETH_ALEN); - } + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } @@ -220,19 +265,23 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) int freq; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Starting short listen state (state=%s)", + p2p_dbg(p2p, "Starting short listen state (state=%s)", p2p_state_txt(p2p->state)); - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + if (p2p->pending_listen_freq) { + /* We have a pending p2p_listen request */ + p2p_dbg(p2p, "p2p_listen command pending already"); + return; + } + + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); return; } - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + p2p->min_disc_int) * 100; if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) @@ -243,24 +292,22 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) tu = p2p->cfg->max_listen * 1000 / 1024; if (tu == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip listen state " - "since duration was 0 TU"); + p2p_dbg(p2p, "Skip listen state since duration was 0 TU"); p2p_set_timeout(p2p, 0, 0); return; } - p2p->pending_listen_freq = freq; - p2p->pending_listen_sec = 0; - p2p->pending_listen_usec = 1024 * tu; - ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) return; + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = 0; + p2p->pending_listen_usec = 1024 * tu; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode"); + p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; } wpabuf_free(ies); @@ -272,30 +319,29 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) int freq; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Going to listen(only) state"); + p2p_dbg(p2p, "Going to listen(only) state"); + + if (p2p->pending_listen_freq) { + /* We have a pending p2p_listen request */ + p2p_dbg(p2p, "p2p_listen command pending already"); + return -1; + } - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); return -1; } - p2p->pending_listen_freq = freq; p2p->pending_listen_sec = timeout / 1000; p2p->pending_listen_usec = (timeout % 1000) * 1000; if (p2p->p2p_scan_running) { if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - connect is already " - "pending - skip listen"); + p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen"); return 0; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - delay start of listen state"); + p2p_dbg(p2p, "p2p_scan running - delay start of listen state"); p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; return 0; } @@ -304,9 +350,10 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) if (ies == NULL) return -1; + p2p->pending_listen_freq = freq; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode"); + p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; wpabuf_free(ies); return -1; @@ -322,8 +369,10 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) static void p2p_device_clear_reported(struct p2p_data *p2p) { struct p2p_device *dev; - dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { dev->flags &= ~P2P_DEV_REPORTED; + dev->sd_reqs = 0; + } } @@ -384,13 +433,11 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { count++; if (oldest == NULL || - os_time_before(&dev->last_seen, &oldest->last_seen)) + os_reltime_before(&dev->last_seen, &oldest->last_seen)) oldest = dev; } if (count + 1 > p2p->cfg->max_peers && oldest) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove oldest peer entry to make room for a new " - "peer"); + p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer"); dl_list_del(&oldest->list); p2p_device_free(p2p, oldest); } @@ -489,7 +536,7 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); @@ -499,8 +546,8 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, } -static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, - const struct p2p_message *msg) +static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev, + int probe_req, const struct p2p_message *msg) { os_memcpy(dev->info.device_name, msg->device_name, sizeof(dev->info.device_name)); @@ -570,9 +617,77 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, } if (!probe_req) { - dev->info.config_methods = msg->config_methods ? + u16 new_config_methods; + new_config_methods = msg->config_methods ? msg->config_methods : msg->wps_config_methods; + if (new_config_methods && + dev->info.config_methods != new_config_methods) { + p2p_dbg(p2p, "Update peer " MACSTR + " config_methods 0x%x -> 0x%x", + MAC2STR(dev->info.p2p_device_addr), + dev->info.config_methods, + new_config_methods); + dev->info.config_methods = new_config_methods; + } + } +} + + +static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, + size_t ies_len) +{ + const u8 *pos, *end; + u8 id, len; + + wpabuf_free(dev->info.vendor_elems); + dev->info.vendor_elems = NULL; + + end = ies + ies_len; + + for (pos = ies; pos + 1 < end; pos += len) { + id = *pos++; + len = *pos++; + + if (pos + len > end) + break; + + if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3) + continue; + + if (len >= 4) { + u32 type = WPA_GET_BE32(pos); + + if (type == WPA_IE_VENDOR_TYPE || + type == WMM_IE_VENDOR_TYPE || + type == WPS_IE_VENDOR_TYPE || + type == P2P_IE_VENDOR_TYPE || + type == WFD_IE_VENDOR_TYPE) + continue; + } + + /* Unknown vendor element - make raw IE data available */ + if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0) + break; + wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len); + } +} + + +static int p2p_compare_wfd_info(struct p2p_device *dev, + const struct p2p_message *msg) +{ + if (dev->info.wfd_subelems && msg->wfd_subelems) { + if (dev->info.wfd_subelems->used != msg->wfd_subelems->used) + return 1; + + return os_memcmp(dev->info.wfd_subelems->buf, + msg->wfd_subelems->buf, + dev->info.wfd_subelems->used); } + if (dev->info.wfd_subelems || msg->wfd_subelems) + return 1; + + return 0; } @@ -583,7 +698,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, * P2P Device Address or P2P Interface Address) * @level: Signal level (signal strength of the received frame from the peer) * @freq: Frequency on which the Beacon or Probe Response frame was received - * @age_ms: Age of the information in milliseconds + * @rx_time: Time when the result was received * @ies: IEs from the Beacon or Probe Response frame * @ies_len: Length of ies buffer in octets * @scan_res: Whether this was based on scan results @@ -595,19 +710,19 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, * Info attributes. */ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, - unsigned int age_ms, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res) { struct p2p_device *dev; struct p2p_message msg; const u8 *p2p_dev_addr; + int wfd_changed; int i; - struct os_time time_now, time_tmp_age, entry_ts; + struct os_reltime time_now; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ies, ies_len, &msg)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P IE for a device entry"); + p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); p2p_parse_free(&msg); return -1; } @@ -617,18 +732,15 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, else if (msg.device_id) p2p_dev_addr = msg.device_id; else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore scan data without P2P Device Info or " - "P2P Device Id"); + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (!is_zero_ether_addr(p2p->peer_filter) && os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer " - "filter for " MACSTR " due to peer filter", - MAC2STR(p2p_dev_addr)); + p2p_dbg(p2p, "Do not add peer filter for " MACSTR + " due to peer filter", MAC2STR(p2p_dev_addr)); p2p_parse_free(&msg); return 0; } @@ -639,22 +751,27 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, return -1; } - os_get_time(&time_now); - time_tmp_age.sec = age_ms / 1000; - time_tmp_age.usec = (age_ms % 1000) * 1000; - os_time_sub(&time_now, &time_tmp_age, &entry_ts); + if (rx_time == NULL) { + os_get_reltime(&time_now); + rx_time = &time_now; + } /* * Update the device entry only if the new peer * entry is newer than the one previously stored. */ - if (dev->last_seen.usec > 0 && - os_time_before(&entry_ts, &dev->last_seen)) { + if (dev->last_seen.sec > 0 && + os_reltime_before(rx_time, &dev->last_seen)) { + p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec, + (unsigned int) dev->last_seen.sec, + (unsigned int) dev->last_seen.usec); p2p_parse_free(&msg); return -1; } - os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time)); + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); @@ -668,6 +785,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, dev->oper_ssid_len = msg.ssid[1]; } + if (msg.adv_service_instance && msg.adv_service_instance_len) { + wpabuf_free(dev->info.p2ps_instance); + dev->info.p2ps_instance = wpabuf_alloc_copy( + msg.adv_service_instance, msg.adv_service_instance_len); + } + if (freq >= 2412 && freq <= 2484 && msg.ds_params && *msg.ds_params >= 1 && *msg.ds_params <= 14) { int ds_freq; @@ -676,18 +799,15 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, else ds_freq = 2407 + *msg.ds_params * 5; if (freq != ds_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Update Listen frequency based on DS " - "Parameter Set IE: %d -> %d MHz", + p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz", freq, ds_freq); freq = ds_freq; } } if (dev->listen_freq && dev->listen_freq != freq && scan_res) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Update Listen frequency based on scan " - "results (" MACSTR " %d -> %d MHz (DS param %d)", + p2p_dbg(p2p, "Update Listen frequency based on scan results (" + MACSTR " %d -> %d MHz (DS param %d)", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq, msg.ds_params ? *msg.ds_params : -1); } @@ -698,7 +818,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, } dev->info.level = level; - p2p_copy_wps_info(dev, 0, &msg); + p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); @@ -714,6 +834,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, break; } + wfd_changed = p2p_compare_wfd_info(dev, &msg); + if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); @@ -726,17 +848,39 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_parse_free(&msg); - if (p2p_pending_sd_req(p2p, dev)) - dev->flags |= P2P_DEV_SD_SCHEDULE; + p2p_update_peer_vendor_elems(dev, ies, ies_len); - if (dev->flags & P2P_DEV_REPORTED) + if (dev->flags & P2P_DEV_REPORTED && !wfd_changed && + (!msg.adv_service_instance || + (dev->flags & P2P_DEV_P2PS_REPORTED))) return 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer found with Listen frequency %d MHz", freq); + p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", + freq, (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); if (dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not report rejected device"); + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (dev->info.config_methods == 0 && + (freq == 2412 || freq == 2437 || freq == 2462)) { + /* + * If we have only seen a Beacon frame from a GO, we do not yet + * know what WPS config methods it supports. Since some + * applications use config_methods value from P2P-DEVICE-FOUND + * events, postpone reporting this peer until we've fully + * discovered its capabilities. + * + * At least for now, do this only if the peer was detected on + * one of the social channels since that peer can be easily be + * found again and there are no limitations of having to use + * passive scan on this channels, so this can be done through + * Probe Response frame that includes the config_methods + * information. + */ + p2p_dbg(p2p, "Do not report peer " MACSTR + " with unknown config methods", MAC2STR(addr)); return 0; } @@ -744,6 +888,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, !(dev->flags & P2P_DEV_REPORTED_ONCE)); dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + if (msg.adv_service_instance) + dev->flags |= P2P_DEV_P2PS_REPORTED; + return 0; } @@ -756,8 +903,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) /* * If GO Negotiation is in progress, report that it has failed. */ - p2p_go_neg_failed(p2p, dev, -1); - p2p->go_neg_peer = NULL; + p2p_go_neg_failed(p2p, -1); } if (p2p->invite_peer == dev) p2p->invite_peer = NULL; @@ -777,6 +923,9 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) } wpabuf_free(dev->info.wfd_subelems); + wpabuf_free(dev->info.vendor_elems); + wpabuf_free(dev->go_neg_conf); + wpabuf_free(dev->info.p2ps_instance); os_free(dev); } @@ -824,9 +973,8 @@ static int p2p_get_next_prog_freq(struct p2p_data *p2p) channel = c->reg_class[cl].channel[ch]; } - freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search " - "channel: reg_class %u channel %u -> %d MHz", + freq = p2p_channel_to_freq(reg_class, channel); + p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz", reg_class, channel, freq); p2p->last_prog_scan_class = reg_class; p2p->last_prog_scan_chan = channel; @@ -845,9 +993,7 @@ static void p2p_search(struct p2p_data *p2p) int res; if (p2p->drv_in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " - "in Listen state - wait for it to end before " - "continuing"); + p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing"); return; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); @@ -855,31 +1001,18 @@ static void p2p_search(struct p2p_data *p2p) if (p2p->find_type == P2P_FIND_PROGRESSIVE && (freq = p2p_get_next_prog_freq(p2p)) > 0) { type = P2P_SCAN_SOCIAL_PLUS_ONE; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "(+ freq %u)", freq); + p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { type = P2P_SCAN_SOCIAL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search"); + p2p_dbg(p2p, "Starting search"); } res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, p2p->num_req_dev_types, p2p->req_dev_types, p2p->find_dev_id, pw_id); if (res < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Scan request failed"); + p2p_dbg(p2p, "Scan request schedule failed"); p2p_continue_find(p2p); - } else if (res == 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " - "p2p_scan at this point - will try again after " - "previous scan completes"); - p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY); - } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); - p2p->p2p_scan_running = 1; - eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); - eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, - p2p, NULL); } } @@ -887,21 +1020,35 @@ static void p2p_search(struct p2p_data *p2p) static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop"); + p2p_dbg(p2p, "Find timeout -> stop"); p2p_stop_find(p2p); } +void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status) +{ + if (status != 0) { + p2p_dbg(p2p, "Scan request failed"); + /* Do continue find even for the first p2p_find_scan */ + p2p_continue_find(p2p); + } else { + p2p_dbg(p2p, "Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } +} + + static int p2p_run_after_scan(struct p2p_data *p2p) { struct p2p_device *dev; enum p2p_after_scan op; if (p2p->after_scan_tx) { - /* TODO: schedule p2p_run_after_scan to be called from TX - * status callback(?) */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending " - "Action frame at p2p_scan completion"); + p2p->after_scan_tx_in_progress = 1; + p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion"); p2p->cfg->send_action(p2p->cfg->cb_ctx, p2p->after_scan_tx->freq, p2p->after_scan_tx->dst, @@ -921,19 +1068,16 @@ static int p2p_run_after_scan(struct p2p_data *p2p) case P2P_AFTER_SCAN_NOTHING: break; case P2P_AFTER_SCAN_LISTEN: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " - "requested Listen state"); + p2p_dbg(p2p, "Start previously requested Listen state"); p2p_listen(p2p, p2p->pending_listen_sec * 1000 + p2p->pending_listen_usec / 1000); return 1; case P2P_AFTER_SCAN_CONNECT: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " - "requested connect with " MACSTR, + p2p_dbg(p2p, "Start previously requested connect with " MACSTR, MAC2STR(p2p->after_scan_peer)); dev = p2p_get_device(p2p, p2p->after_scan_peer); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not " - "known anymore"); + p2p_dbg(p2p, "Peer not known anymore"); break; } p2p_connect_send(p2p, dev); @@ -948,8 +1092,7 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; int running; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout " - "(running=%d)", p2p->p2p_scan_running); + p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running); running = p2p->p2p_scan_running; /* Make sure we recover from missed scan results callback */ p2p->p2p_scan_running = 0; @@ -967,18 +1110,51 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p) } +static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash) +{ + u8 buf[SHA256_MAC_LEN]; + char str_buf[256]; + const u8 *adv_array; + size_t i, adv_len; + + if (!str || !hash) + return 0; + + if (!str[0]) { + os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN); + return 1; + } + + adv_array = (u8 *) str_buf; + adv_len = os_strlen(str); + + for (i = 0; str[i] && i < adv_len; i++) { + if (str[i] >= 'A' && str[i] <= 'Z') + str_buf[i] = str[i] - 'A' + 'a'; + else + str_buf[i] = str[i]; + } + + if (sha256_vector(1, &adv_array, &adv_len, buf)) + return 0; + + os_memcpy(hash, buf, P2PS_HASH_LEN); + return 1; +} + + 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) + const u8 *dev_id, unsigned int search_delay, + u8 seek_count, const char **seek, int freq) { int res; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)", - type); + p2p_dbg(p2p, "Starting find (type=%d)", type); + os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is " - "already running"); + p2p_dbg(p2p, "p2p_scan is already running"); } p2p_free_req_dev_types(p2p); @@ -998,6 +1174,47 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, } else p2p->find_dev_id = NULL; + if (seek_count == 0 || !seek) { + /* Not an ASP search */ + p2p->p2ps_seek = 0; + } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) { + /* + * An empty seek string means no hash values, but still an ASP + * search. + */ + p2p->p2ps_seek_count = 0; + p2p->p2ps_seek = 1; + } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) { + u8 buf[P2PS_HASH_LEN]; + int i; + + p2p->p2ps_seek_count = seek_count; + for (i = 0; i < seek_count; i++) { + if (!p2ps_gen_hash(p2p, seek[i], buf)) + continue; + + /* If asking for wildcard, don't do others */ + if (os_memcmp(buf, p2p->wild_card_hash, + P2PS_HASH_LEN) == 0) { + p2p->p2ps_seek_count = 0; + break; + } + + os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf, + P2PS_HASH_LEN); + } + p2p->p2ps_seek = 1; + } else { + p2p->p2ps_seek_count = 0; + p2p->p2ps_seek = 1; + } + + /* Special case to perform wildcard search */ + if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) { + p2p->p2ps_seek_count = 1; + os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN); + } + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); @@ -1013,6 +1230,19 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p, NULL); switch (type) { case P2P_FIND_START_WITH_FULL: + if (freq > 0) { + /* + * Start with the specified channel and then move to + * social channels only scans. + */ + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, + P2P_SCAN_SPECIFIC, freq, + p2p->num_req_dev_types, + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); + break; + } + /* fall through */ case P2P_FIND_PROGRESSIVE: res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, p2p->num_req_dev_types, @@ -1029,22 +1259,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } - if (res == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan"); - p2p->p2p_scan_running = 1; - eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); - eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, - p2p, NULL); - } else if (res == 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " - "p2p_scan at this point - will try again after " - "previous scan completes"); - res = 0; - p2p_set_state(p2p, P2P_SEARCH_WHEN_READY); - eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); - } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " - "p2p_scan"); + if (res != 0 && p2p->p2p_scan_running) { + p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); + /* wait for the previous p2p_scan to complete */ + res = 0; /* do not report failure */ + } else if (res != 0) { + p2p_dbg(p2p, "Failed to start p2p_scan"); p2p_set_state(p2p, P2P_IDLE); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); } @@ -1053,47 +1273,33 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, } -int p2p_other_scan_completed(struct p2p_data *p2p) -{ - if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) { - p2p_set_state(p2p, P2P_SEARCH); - p2p_search(p2p); - return 1; - } - if (p2p->state != P2P_SEARCH_WHEN_READY) - return 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find " - "now that previous scan was completed"); - if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, - p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id, p2p->search_delay) < 0) - return 0; - return 1; -} - - void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find"); + p2p_dbg(p2p, "Stopping find"); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p_clear_timeout(p2p); - if (p2p->state == P2P_SEARCH) - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED); + if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND) + p2p->cfg->find_stopped(p2p->cfg->cb_ctx); + + p2p->p2ps_seek_count = 0; + p2p_set_state(p2p, P2P_IDLE); p2p_free_req_dev_types(p2p); p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + if (p2p->go_neg_peer) + p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer = NULL; p2p->sd_peer = NULL; p2p->invite_peer = NULL; p2p_stop_listen_for_freq(p2p, freq); + p2p->send_action_in_progress = 0; } void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) { if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen " - "since we are on correct channel for response"); + p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response"); return; } if (p2p->in_listen) { @@ -1106,38 +1312,51 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) * when the operation gets canceled, so clear the internal * variable that is tracking driver state. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear " - "drv_in_listen (%d)", p2p->drv_in_listen); + p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); p2p->drv_in_listen = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); } +void p2p_stop_listen(struct p2p_data *p2p) +{ + if (p2p->state != P2P_LISTEN_ONLY) { + p2p_dbg(p2p, "Skip stop_listen since not in listen_only state."); + return; + } + + p2p_stop_listen_for_freq(p2p, 0); + p2p_set_state(p2p, P2P_IDLE); +} + + void p2p_stop_find(struct p2p_data *p2p) { + p2p->pending_listen_freq = 0; p2p_stop_find_for_freq(p2p, 0); } static int p2p_prepare_channel_pref(struct p2p_data *p2p, unsigned int force_freq, - unsigned int pref_freq) + unsigned int pref_freq, int go) { u8 op_class, op_channel; unsigned int freq = force_freq ? force_freq : pref_freq; - if (p2p_freq_to_channel(p2p->cfg->country, freq, - &op_class, &op_channel) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported frequency %u MHz", freq); + p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); + if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); return -1; } - if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Frequency %u MHz (oper_class %u channel %u) not " - "allowed for P2P", freq, op_class, op_channel); + if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, + op_channel))) { + p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", + freq, op_class, op_channel); return -1; } @@ -1161,34 +1380,74 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + p2p_dbg(p2p, "Prepare channel best"); if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && p2p_supported_freq(p2p, p2p->best_freq_overall) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall, - &op_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best " - "overall channel as operating channel preference"); + p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best overall channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && p2p_supported_freq(p2p, p2p->best_freq_5) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, - &op_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 5 GHz " - "channel as operating channel preference"); + p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && p2p_supported_freq(p2p, p2p->best_freq_24) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, - &op_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 2.4 " - "GHz channel as operating channel preference"); + p2p_freq_to_channel(p2p->best_freq_24, &op_class, + &op_channel) == 0) { + p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; - } else { + } else if (p2p->cfg->num_pref_chan > 0 && + p2p_channels_includes(&p2p->cfg->channels, + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan)) { + p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference"); + p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class; + p2p->op_channel = p2p->cfg->pref_chan[0].chan; + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channels_includes(&p2p->cfg->channels, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel)) { + p2p_dbg(p2p, "Select pre-configured channel as operating channel preference"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; + } else if (p2p_channel_random_social(&p2p->cfg->channels, + &p2p->op_reg_class, + &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else { + /* Select any random available channel from the first available + * operating class */ + p2p_channel_select(&p2p->cfg->channels, NULL, + &p2p->op_reg_class, + &p2p->op_channel); + p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference", + p2p->op_channel, p2p->op_reg_class); } os_memcpy(&p2p->channels, &p2p->cfg->channels, @@ -1202,6 +1461,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced * @pref_freq: Preferred frequency in MHz or 0 if no preference + * @go: Whether the local end will be forced to be GO * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO @@ -1209,18 +1469,27 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * may be further optimized in p2p_reselect_channel() once the peer information * is available. */ -static int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, - unsigned int force_freq, unsigned int pref_freq) +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, int go) { + p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); if (force_freq || pref_freq) { - if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0) + if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) < + 0) return -1; } else { p2p_prepare_channel_best(p2p); } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Own preference for operation channel: " - "Operating Class %u Channel %u%s", + p2p_channels_dump(p2p, "prepared channels", &p2p->channels); + if (go) + p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); + else if (!force_freq) + p2p_channels_union_inplace(&p2p->channels, + &p2p->cfg->cli_channels); + p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels); + + p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", p2p->op_reg_class, p2p->op_channel, force_freq ? " (forced)" : ""); @@ -1258,40 +1527,38 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, 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) + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to start group negotiation - peer=" MACSTR + p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d pd_before_go_neg=%d", + " wps_method=%d persistent_group=%d pd_before_go_neg=%d " + "oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group, pd_before_go_neg); + wps_method, persistent_group, pd_before_go_neg, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to unknown P2P Device " MACSTR, + p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } - if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + go_intent == 15) < 0) return -1; if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to P2P Device " MACSTR + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer_addr)); return -1; } if (dev->oper_freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to P2P Device " MACSTR + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " with incomplete information", MAC2STR(peer_addr)); return -1; @@ -1319,8 +1586,18 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; if (pd_before_go_neg) dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; - else + else { dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + /* + * Assign dialog token and tie breaker here to use the same + * values in each retry within the same GO Negotiation exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + dev->tie_breaker = p2p->next_tie_breaker; + p2p->next_tie_breaker = !p2p->next_tie_breaker; + } dev->connect_reqs = 0; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; @@ -1337,19 +1614,17 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, * new GO Negotiation, e.g., when the pending frame was from a * previous attempt at starting a GO Negotiation. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " - "previous pending Action frame TX that was waiting " - "for p2p_scan completion"); + p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion"); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; } dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - delay connect send"); + p2p_dbg(p2p, "p2p_scan running - delay connect send"); p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); return 0; @@ -1365,26 +1640,25 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, 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) + unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to authorize group negotiation - peer=" MACSTR + p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d", + " wps_method=%d persistent_group=%d oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group); + wps_method, persistent_group, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot authorize unknown P2P Device " MACSTR, + p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } - if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == + 15) < 0) return -1; p2p->ssid_set = 0; @@ -1405,6 +1679,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; return 0; @@ -1414,18 +1689,16 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg) { - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); - p2p_copy_wps_info(dev, 0, msg); + p2p_copy_wps_info(p2p, dev, 0, msg); if (msg->listen_channel) { int freq; - freq = p2p_channel_to_freq((char *) msg->listen_channel, - msg->listen_channel[3], + freq = p2p_channel_to_freq(msg->listen_channel[3], msg->listen_channel[4]); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown peer Listen channel: " + p2p_dbg(p2p, "Unknown peer Listen channel: " "country=%c%c(0x%02x) reg_class=%u channel=%u", msg->listen_channel[0], msg->listen_channel[1], @@ -1433,8 +1706,8 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, msg->listen_channel[3], msg->listen_channel[4]); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update " - "peer " MACSTR " Listen channel: %u -> %u MHz", + p2p_dbg(p2p, "Update peer " MACSTR + " Listen channel: %u -> %u MHz", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq); dev->listen_freq = freq; @@ -1448,12 +1721,9 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Completed device entry based on data from " - "GO Negotiation Request"); + p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request"); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Created device entry based on GO Neg Req: " + p2p_dbg(p2p, "Created device entry based on GO Neg Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " "listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), @@ -1464,8 +1734,7 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; if (dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not report rejected device"); + p2p_dbg(p2p, "Do not report rejected device"); return; } @@ -1487,8 +1756,15 @@ void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) { - p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); - p2p_random(params->passphrase, 8); + if (p2p->ssid_set) { + os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len); + params->ssid_len = p2p->ssid_len; + } else { + p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); + } + p2p->ssid_set = 0; + + p2p_random(params->passphrase, p2p->cfg->passphrase_len); return 0; } @@ -1498,13 +1774,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) struct p2p_go_neg_results res; int go = peer->go_state == LOCAL_GO; struct p2p_channels intersection; - int freqs; - size_t i, j; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR " completed (%s will be " - "GO)", MAC2STR(peer->info.p2p_device_addr), - go ? "local end" : "peer"); + p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", + MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); os_memset(&res, 0, sizeof(res)); res.role_go = go; @@ -1520,12 +1792,11 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) if (go) { /* Setup AP mode for WPS provisioning */ - res.freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + res.freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; - p2p_random(res.passphrase, 8); + p2p_random(res.passphrase, p2p->cfg->passphrase_len); } else { res.freq = peer->oper_freq; if (p2p->ssid_len) { @@ -1534,31 +1805,28 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) } } + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &peer->channels); p2p_channels_intersect(&p2p->channels, &peer->channels, &intersection); - freqs = 0; - for (i = 0; i < intersection.reg_classes; i++) { - struct p2p_reg_class *c = &intersection.reg_class[i]; - if (freqs + 1 == P2P_MAX_CHANNELS) - break; - for (j = 0; j < c->channels; j++) { - int freq; - if (freqs + 1 == P2P_MAX_CHANNELS) - break; - freq = p2p_channel_to_freq(peer->country, c->reg_class, - c->channel[j]); - if (freq < 0) - continue; - res.freq_list[freqs++] = freq; - } + if (go) { + p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", + &intersection); } + p2p_channels_to_freqs(&intersection, res.freq_list, + P2P_MAX_CHANNELS); + res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; peer->wps_method = WPS_NOT_READY; + peer->oob_pw_id = 0; + wpabuf_free(peer->go_neg_conf); + peer->go_neg_conf = NULL; p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); @@ -1568,8 +1836,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); if (len < 1) @@ -1605,8 +1872,7 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported P2P Public Action frame type %d", + p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d", data[0]); break; } @@ -1624,20 +1890,15 @@ static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, case WLAN_PA_VENDOR_SPECIFIC: data++; len--; - if (len < 3) + if (len < 4) return; - if (WPA_GET_BE24(data) != OUI_WFA) + if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) return; - data += 3; - len -= 3; - if (len < 1) - return; - - if (*data != P2P_OUI_TYPE) - return; + data += 4; + len -= 4; - p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq); + p2p_rx_p2p_action(p2p, sa, data, len, freq); break; case WLAN_PA_GAS_INITIAL_REQ: p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); @@ -1670,27 +1931,20 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, if (len < 4) return; - if (WPA_GET_BE24(data) != OUI_WFA) - return; - data += 3; - len -= 3; - - if (*data != P2P_OUI_TYPE) + if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) return; - data++; - len--; + data += 4; + len -= 4; /* P2P action frame */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: RX P2P Action from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_NOA: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - Notice of Absence"); + p2p_dbg(p2p, "Received P2P Action - Notice of Absence"); /* TODO */ break; case P2P_PRESENCE_REQ: @@ -1703,8 +1957,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - unknown type %u", data[0]); + p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]); break; } } @@ -1715,8 +1968,17 @@ static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) struct p2p_data *p2p = eloop_ctx; if (p2p->go_neg_peer == NULL) return; + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->go_neg_peer->status = P2P_SC_SUCCESS; + /* + * Set new timeout to make sure a previously set one does not expire + * too quickly while waiting for the GO Negotiation to complete. + */ + p2p_set_timeout(p2p, 0, 500000); p2p_connect_send(p2p, p2p->go_neg_peer); } @@ -1726,8 +1988,13 @@ static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) struct p2p_data *p2p = eloop_ctx; if (p2p->invite_peer == NULL) return; + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr); + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, + p2p->invite_dev_pw_id); } @@ -1760,7 +2027,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, if (dev) { if (dev->country[0] == 0 && msg.listen_channel) os_memcpy(dev->country, msg.listen_channel, 3); - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ } @@ -1771,17 +2038,16 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, return; } - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); dev->flags |= P2P_DEV_PROBE_REQ_ONLY; if (msg.listen_channel) { os_memcpy(dev->country, msg.listen_channel, 3); - dev->listen_freq = p2p_channel_to_freq(dev->country, - msg.listen_channel[3], + dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); } - p2p_copy_wps_info(dev, 1, &msg); + p2p_copy_wps_info(p2p, dev, 1, &msg); if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); @@ -1790,8 +2056,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, p2p_parse_free(&msg); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Created device entry based on Probe Req: " MACSTR + p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, @@ -1807,7 +2072,7 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, dev = p2p_get_device(p2p, addr); if (dev) { - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); return dev; /* already known */ } @@ -1870,11 +2135,12 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) attr.num_req_dev_type)) return 1; /* Own Primary Device Type matches */ - for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) { if (dev_type_list_match(p2p->cfg->sec_dev_type[i], attr.req_dev_type, attr.num_req_dev_type)) - return 1; /* Own Secondary Device Type matches */ + return 1; /* Own Secondary Device Type matches */ + } /* No matching device type found */ return 0; @@ -1893,6 +2159,12 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) extra = wpabuf_len(p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); + + if (p2p->query_count) + extra += MAX_SVC_ADV_IE_LEN; + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -1902,13 +2174,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); } - p2p_build_wps_ie(p2p, buf, pw_id, 1); + if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for Probe Response"); + wpabuf_free(buf); + return NULL; + } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_resp) wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) + wpabuf_put_buf(buf, + p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); + /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & @@ -1919,40 +2199,35 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) p2p_buf_add_device_info(buf, p2p, NULL); p2p_buf_update_ie_hdr(buf, len); - return buf; -} - + if (p2p->query_count) { + p2p_buf_add_service_instance(buf, p2p, p2p->query_count, + p2p->query_hash, + p2p->p2ps_adv_list); + } -static int is_11b(u8 rate) -{ - return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; + return buf; } -static int supp_rates_11b_only(struct ieee802_11_elems *elems) +static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { - int num_11b = 0, num_others = 0; - int i; + struct p2ps_advertisement *adv_data; - if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) - return 0; + p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); - for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { - if (is_11b(elems->supp_rates[i])) - num_11b++; - else - num_others++; - } + /* Wildcard always matches if we have actual services */ + if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0) + return p2p->p2ps_adv_list != NULL; - 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++; + adv_data = p2p->p2ps_adv_list; + while (adv_data) { + p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]); + if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0) + return 1; + adv_data = adv_data->next; } - return num_11b > 0 && num_others == 0; + return 0; } @@ -1966,19 +2241,16 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, struct p2p_message msg; struct wpabuf *ies; - if (!p2p->in_listen || !p2p->drv_in_listen) { - /* not in Listen state - ignore Probe Request */ - return P2P_PREQ_NOT_LISTEN; - } - if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ + p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it"); return P2P_PREQ_MALFORMED; } if (elems.p2p == NULL) { /* not a P2P probe - ignore it */ + p2p_dbg(p2p, "Not a P2P probe - ignore it"); return P2P_PREQ_NOT_P2P; } @@ -1986,11 +2258,15 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Not sent to the broadcast address or our P2P Device Address */ + p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it", + MAC2STR(dst)); return P2P_PREQ_NOT_PROCESSED; } if (bssid && !is_broadcast_ether_addr(bssid)) { /* Not sent to the Wildcard BSSID */ + p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it", + MAC2STR(bssid)); return P2P_PREQ_NOT_PROCESSED; } @@ -1998,23 +2274,86 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* not using P2P Wildcard SSID - ignore */ + p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it"); return P2P_PREQ_NOT_PROCESSED; } if (supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ + p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it"); return P2P_PREQ_NOT_P2P; } os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0) { /* Could not parse P2P attributes */ + p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it"); return P2P_PREQ_NOT_P2P; } + p2p->p2ps_svc_found = 0; + + if (msg.service_hash && msg.service_hash_count) { + const u8 *hash = msg.service_hash; + u8 *dest = p2p->query_hash; + u8 i; + + p2p->query_count = 0; + for (i = 0; i < msg.service_hash_count; i++) { + if (p2p_service_find_asp(p2p, hash)) { + p2p->p2ps_svc_found = 1; + + if (!os_memcmp(hash, p2p->wild_card_hash, + P2PS_HASH_LEN)) { + /* We found match(es) but wildcard + * will return all */ + p2p->query_count = 1; + os_memcpy(p2p->query_hash, hash, + P2PS_HASH_LEN); + break; + } + + /* Save each matching hash */ + if (p2p->query_count < P2P_MAX_QUERY_HASH) { + os_memcpy(dest, hash, P2PS_HASH_LEN); + dest += P2PS_HASH_LEN; + p2p->query_count++; + } else { + /* We found match(es) but too many to + * return all */ + p2p->query_count = 0; + break; + } + } + hash += P2PS_HASH_LEN; + } + + p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found); + + /* Probed hash unknown */ + if (!p2p->p2ps_svc_found) { + p2p_parse_free(&msg); + return P2P_PREQ_NOT_PROCESSED; + } + } else { + /* This is not a P2PS Probe Request */ + p2p->query_count = 0; + p2p_dbg(p2p, "No P2PS Hash in Probe Request"); + + if (!p2p->in_listen || !p2p->drv_in_listen) { + /* not in Listen state - ignore Probe Request */ + p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request", + p2p->in_listen, p2p->drv_in_listen); + p2p_parse_free(&msg); + return P2P_PREQ_NOT_LISTEN; + } + } + if (msg.device_id && os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Device ID did not match */ + p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it", + MAC2STR(msg.device_id)); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2023,6 +2362,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, if (msg.wps_attributes && !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ + p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2030,11 +2370,11 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ + p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); return P2P_PREQ_NOT_PROCESSED; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Reply to P2P Probe Request in Listen state"); + p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state"); /* * We do not really have a specific BSS that this frame is advertising, @@ -2106,27 +2446,28 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); + p2p->query_count = 0; if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && p2p->go_neg_peer && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) - == 0) { + == 0 && + !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { /* Received a Probe Request from GO Negotiation peer */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found GO Negotiation peer - try to start GO " - "negotiation from timeout"); + p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); return P2P_PREQ_PROCESSED; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && p2p->invite_peer && + (p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) && os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) == 0) { /* Received a Probe Request from Invite peer */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found Invite peer - try to start Invite from " - "timeout"); + p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); + eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); return P2P_PREQ_PROCESSED; } @@ -2203,6 +2544,9 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, extra = wpabuf_len(p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]); + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) @@ -2218,6 +2562,10 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]) + wpabuf_put_buf(tmp, + p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]); + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; lpos = p2p_buf_add_ie_hdr(tmp); @@ -2256,19 +2604,145 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) } -int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) +struct p2ps_advertisement * +p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id) { - struct p2p_message msg; + struct p2ps_advertisement *adv_data; - os_memset(&msg, 0, sizeof(msg)); - if (p2p_parse_p2p_ie(p2p_ie, &msg)) - return -1; + if (!p2p) + return NULL; - if (msg.p2p_device_addr) { - os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); - return 0; - } else if (msg.device_id) { - os_memcpy(dev_addr, msg.device_id, ETH_ALEN); + adv_data = p2p->p2ps_adv_list; + while (adv_data) { + if (adv_data->id == adv_id) + return adv_data; + adv_data = adv_data->next; + } + + return NULL; +} + + +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id) +{ + struct p2ps_advertisement *adv_data; + struct p2ps_advertisement **prior; + + if (!p2p) + return -1; + + adv_data = p2p->p2ps_adv_list; + prior = &p2p->p2ps_adv_list; + while (adv_data) { + if (adv_data->id == adv_id) { + p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id); + *prior = adv_data->next; + os_free(adv_data); + return 0; + } + prior = &adv_data->next; + adv_data = adv_data->next; + } + + return -1; +} + + +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) +{ + struct p2ps_advertisement *adv_data, *tmp, **prev; + u8 buf[P2PS_HASH_LEN]; + size_t adv_data_len, adv_len, info_len = 0; + + if (!p2p || !adv_str || !adv_str[0]) + return -1; + + if (!(config_methods & p2p->cfg->config_methods)) { + p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x", + config_methods, p2p->cfg->config_methods); + return -1; + } + + if (!p2ps_gen_hash(p2p, adv_str, buf)) + return -1; + + if (svc_info) + info_len = os_strlen(svc_info); + adv_len = os_strlen(adv_str); + adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 + + info_len + 1; + + adv_data = os_zalloc(adv_data_len); + if (!adv_data) + return -1; + + os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN); + adv_data->id = adv_id; + adv_data->state = svc_state; + adv_data->config_methods = config_methods & p2p->cfg->config_methods; + adv_data->auto_accept = (u8) auto_accept; + os_memcpy(adv_data->svc_name, adv_str, adv_len); + + if (svc_info && info_len) { + adv_data->svc_info = &adv_data->svc_name[adv_len + 1]; + os_memcpy(adv_data->svc_info, svc_info, info_len); + } + + /* + * Group Advertisements by service string. They do not need to be + * sorted, but groups allow easier Probe Response instance grouping + */ + tmp = p2p->p2ps_adv_list; + prev = &p2p->p2ps_adv_list; + while (tmp) { + if (tmp->id == adv_data->id) { + if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) { + os_free(adv_data); + return -1; + } + adv_data->next = tmp->next; + *prev = adv_data; + os_free(tmp); + goto inserted; + } else { + if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) { + adv_data->next = tmp->next; + tmp->next = adv_data; + goto inserted; + } + } + prev = &tmp->next; + tmp = tmp->next; + } + + /* No svc_name match found */ + adv_data->next = p2p->p2ps_adv_list; + p2p->p2ps_adv_list = adv_data; + +inserted: + p2p_dbg(p2p, + "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'", + adv_id, adv_data->config_methods, svc_state, adv_str); + + return 0; +} + + +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return -1; + + if (msg.p2p_device_addr) { + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + return 0; + } else if (msg.device_id) { + os_memcpy(dev_addr, msg.device_id, ETH_ALEN); return 0; } return -1; @@ -2301,24 +2775,20 @@ static void p2p_clear_go_neg(struct p2p_data *p2p) void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) { if (p2p->go_neg_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Group Formation - " - "ignore WPS registration success notification"); + p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification"); return; /* No pending Group Formation */ } if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore WPS registration success notification " - "for " MACSTR " (GO Negotiation peer " MACSTR ")", + p2p_dbg(p2p, "Ignore WPS registration success notification for " + MACSTR " (GO Negotiation peer " MACSTR ")", MAC2STR(mac_addr), MAC2STR(p2p->go_neg_peer->intended_addr)); return; /* Ignore unexpected peer address */ } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Group Formation completed successfully with " MACSTR, + p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR, MAC2STR(mac_addr)); p2p_clear_go_neg(p2p); @@ -2328,14 +2798,11 @@ void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) void p2p_group_formation_failed(struct p2p_data *p2p) { if (p2p->go_neg_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Group Formation - " - "ignore group formation failure notification"); + p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification"); return; /* No pending Group Formation */ } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Group Formation failed with " MACSTR, + p2p_dbg(p2p, "Group Formation failed with " MACSTR, MAC2STR(p2p->go_neg_peer->intended_addr)); p2p_clear_go_neg(p2p); @@ -2346,7 +2813,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) { struct p2p_data *p2p; - if (cfg->max_peers < 1) + if (cfg->max_peers < 1 || + cfg->passphrase_len < 8 || cfg->passphrase_len > 63) return NULL; p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); @@ -2375,11 +2843,14 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->cfg->num_pref_chan = 0; } + p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash); + p2p->min_disc_int = 1; p2p->max_disc_int = 3; p2p->max_disc_tu = -1; - os_get_random(&p2p->next_tie_breaker, 1); + if (os_get_random(&p2p->next_tie_breaker, 1) < 0) + p2p->next_tie_breaker = 0; p2p->next_tie_breaker &= 0x01; if (cfg->sd_request) p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; @@ -2395,6 +2866,11 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->go_timeout = 100; p2p->client_timeout = 20; + p2p->num_p2p_sd_queries = 0; + + p2p_dbg(p2p, "initialized"); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); return p2p; } @@ -2402,6 +2878,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_deinit(struct p2p_data *p2p) { + struct p2ps_advertisement *adv, *prev; + #ifdef CONFIG_WIFI_DISPLAY wpabuf_free(p2p->wfd_ie_beacon); wpabuf_free(p2p->wfd_ie_probe_req); @@ -2419,6 +2897,8 @@ void p2p_deinit(struct p2p_data *p2p) eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); p2p_flush(p2p); p2p_free_req_dev_types(p2p); os_free(p2p->cfg->dev_name); @@ -2428,9 +2908,19 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->cfg->serial_number); os_free(p2p->cfg->pref_chan); os_free(p2p->groups); + os_free(p2p->p2ps_prov); wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); + os_free(p2p->no_go_freq.range); + + adv = p2p->p2ps_adv_list; + while (adv) { + prev = adv; + adv = adv->next; + os_free(prev); + } + os_free(p2p); } @@ -2458,13 +2948,15 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) if (dev == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR, - MAC2STR(addr)); + p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr)); - if (p2p->go_neg_peer == dev) + if (p2p->go_neg_peer == dev) { + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); p2p->go_neg_peer = NULL; + } dev->wps_method = WPS_NOT_READY; + dev->oob_pw_id = 0; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; @@ -2619,55 +3111,105 @@ int p2p_set_country(struct p2p_data *p2p, const char *country) } +static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) +{ + if (dev->sd_pending_bcast_queries == 0) { + /* Initialize with total number of registered broadcast + * SD queries. */ + dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries; + } + + if (p2p_start_sd(p2p, dev) == 0) + return 1; + + if (dev->req_config_methods && + !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { + p2p_dbg(p2p, "Send pending Provision Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) + return 1; + } + + return 0; +} + + void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; + int found; + p2p_set_state(p2p, P2P_SEARCH); + + /* Continue from the device following the last iteration */ + found = 0; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { - if (dev->flags & P2P_DEV_SD_SCHEDULE) { - if (p2p_start_sd(p2p, dev) == 0) - return; - else - break; - } else if (dev->req_config_methods && - !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " - "pending Provision Discovery Request to " - MACSTR " (config methods 0x%x)", - MAC2STR(dev->info.p2p_device_addr), - dev->req_config_methods); - if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) - return; + if (dev == p2p->last_p2p_find_oper) { + found = 1; + continue; + } + if (!found) + continue; + if (p2p_pre_find_operation(p2p, dev) > 0) { + p2p->last_p2p_find_oper = dev; + return; } } + /* + * Wrap around to the beginning of the list and continue until the last + * iteration device. + */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (p2p_pre_find_operation(p2p, dev) > 0) { + p2p->last_p2p_find_oper = dev; + return; + } + if (dev == p2p->last_p2p_find_oper) + break; + } + p2p_listen_in_find(p2p, 1); } static void p2p_sd_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery Query TX callback: success=%d", + p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", success); p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { - if (p2p->sd_peer) { - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; - p2p->sd_peer = NULL; - } - p2p_continue_find(p2p); + if (p2p->sd_peer) + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->sd_peer = NULL; + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); return; } if (p2p->sd_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No SD peer entry known"); - p2p_continue_find(p2p); + p2p_dbg(p2p, "No SD peer entry known"); + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); return; } + if (p2p->sd_query && p2p->sd_query->for_all_peers) { + /* Update the pending broadcast SD query count for this device + */ + p2p->sd_peer->sd_pending_bcast_queries--; + + /* + * If there are no pending broadcast queries for this device, + * mark it as done (-1). + */ + if (p2p->sd_peer->sd_pending_bcast_queries == 0) + p2p->sd_peer->sd_pending_bcast_queries = -1; + } + /* Wait for response from the peer */ p2p_set_state(p2p, P2P_SD_DURING_FIND); p2p_set_timeout(p2p, 0, 200000); @@ -2682,9 +3224,6 @@ static void p2p_retry_pd(struct p2p_data *p2p) { struct p2p_device *dev; - if (p2p->state != P2P_IDLE) - return; - /* * Retry the prov disc req attempt only for the peer that the user had * requested. @@ -2697,13 +3236,13 @@ static void p2p_retry_pd(struct p2p_data *p2p) if (!dev->req_config_methods) continue; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " - "pending Provision Discovery Request to " + p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); p2p_send_prov_disc_req(p2p, dev, - dev->flags & P2P_DEV_PD_FOR_JOIN, 0); + dev->flags & P2P_DEV_PD_FOR_JOIN, + p2p->pd_force_freq); return; } } @@ -2711,8 +3250,7 @@ static void p2p_retry_pd(struct p2p_data *p2p) static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request TX callback: success=%d", + p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d", success); /* @@ -2759,11 +3297,71 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) } +static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p) +{ + if (p2p->after_scan_tx_in_progress) { + p2p->after_scan_tx_in_progress = 0; + if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && + p2p_run_after_scan(p2p)) + return 1; + if (p2p->state == P2P_SEARCH) { + p2p_dbg(p2p, "Continue find after after_scan_tx completion"); + p2p_continue_find(p2p); + } + } + + return 0; +} + + +static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d", + success); + + if (p2p->send_action_in_progress) { + p2p->send_action_in_progress = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (!success) + goto continue_search; + + if (!p2p->cfg->prov_disc_resp_cb || + p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1) + goto continue_search; + + p2p_dbg(p2p, + "Post-Provision Discovery operations started - do not try to continue other P2P operations"); + return; + +continue_search: + p2p_check_after_scan_tx_continuation(p2p); +} + + int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, - unsigned int age, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len) { - p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1); + if (os_reltime_before(rx_time, &p2p->find_start)) { + /* + * The driver may have cached (e.g., in cfg80211 BSS table) the + * scan results for relatively long time. To avoid reporting + * stale information, update P2P peers only based on results + * that have based on frames received after the last p2p_find + * operation was started. + */ + p2p_dbg(p2p, "Ignore old scan result for " MACSTR + " (rx_time=%u.%06u)", + MAC2STR(bssid), (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); + return 0; + } + + p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1); return 0; } @@ -2772,8 +3370,7 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, void p2p_scan_res_handled(struct p2p_data *p2p) { if (!p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not " - "running, but scan results received"); + p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); } p2p->p2p_scan_running = 0; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); @@ -2787,6 +3384,7 @@ void p2p_scan_res_handled(struct p2p_data *p2p) void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) { + u8 dev_capab; u8 *len; #ifdef CONFIG_WIFI_DISPLAY @@ -2794,9 +3392,20 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]) + wpabuf_put_buf(ies, + p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]); + len = p2p_buf_add_ie_hdr(ies); - p2p_buf_add_capability(ies, p2p->dev_capab & - ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + + dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + + /* P2PS requires Probe Request frames to include SD bit */ + if (p2p->p2ps_seek && p2p->p2ps_seek_count) + dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; + + p2p_buf_add_capability(ies, dev_capab, 0); + if (dev_id) p2p_buf_add_device_id(ies, dev_id); if (p2p->cfg->reg_class && p2p->cfg->channel) @@ -2806,6 +3415,10 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) if (p2p->ext_listen_interval) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); + + if (p2p->p2ps_seek && p2p->p2ps_seek_count) + p2p_buf_add_service_hash(ies, p2p); + /* TODO: p2p_buf_add_operating_channel() if GO */ p2p_buf_update_ie_hdr(ies, len); } @@ -2820,6 +3433,10 @@ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) len += wpabuf_len(p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p && p2p->vendor_elem && + p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]) + len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]); + return len; } @@ -2833,14 +3450,12 @@ int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) { struct p2p_device *dev = p2p->go_neg_peer; + int timeout; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Request TX callback: success=%d", - success); + p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending GO Negotiation"); + p2p_dbg(p2p, "No pending GO Negotiation"); return; } @@ -2857,9 +3472,7 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) if (!success && (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && !is_zero_ether_addr(dev->member_in_go_dev)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer " MACSTR " did not acknowledge request - " - "try to use device discoverability through its GO", + p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO", MAC2STR(dev->info.p2p_device_addr)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_send_dev_disc_req(p2p, dev); @@ -2871,35 +3484,56 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) * channel. */ p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, success ? 200000 : 100000); + timeout = success ? 500000 : 100000; + if (!success && p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + unsigned int r; + /* + * Peer is expected to wait our response and we will skip the + * listen phase. Add some randomness to the wait time here to + * make it less likely to hit cases where we could end up in + * sync with peer not listening. + */ + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; + timeout += r % 100000; + } + p2p_set_timeout(p2p, 0, timeout); } static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Response TX callback: success=%d", + p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d", success); if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore TX callback event - GO Negotiation is " - "not running anymore"); + p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore"); return; } p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, 250000); + p2p_set_timeout(p2p, 0, 500000); } -static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success) +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, + const u8 *addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Response (failure) TX callback: " - "success=%d", success); + p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success); if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { - p2p_go_neg_failed(p2p, p2p->go_neg_peer, - p2p->go_neg_peer->status); + p2p_go_neg_failed(p2p, p2p->go_neg_peer->status); + return; } + + if (success) { + struct p2p_device *dev; + dev = p2p_get_device(p2p, addr); + if (dev && + dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; + } + + if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND) + p2p_continue_find(p2p); } @@ -2908,15 +3542,44 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Confirm TX callback: result=%d", - result); - p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); if (result == P2P_SEND_ACTION_FAILED) { - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_go_neg_failed(p2p, -1); return; } + + dev = p2p->go_neg_peer; + if (result == P2P_SEND_ACTION_NO_ACK) { + /* + * Retry GO Negotiation Confirmation + * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive + * ACK for confirmation. + */ + if (dev && dev->go_neg_conf && + dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) { + p2p_dbg(p2p, "GO Negotiation Confirm retry %d", + dev->go_neg_conf_sent); + p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; + if (p2p_send_action(p2p, dev->go_neg_conf_freq, + dev->info.p2p_device_addr, + p2p->cfg->dev_addr, + dev->info.p2p_device_addr, + wpabuf_head(dev->go_neg_conf), + wpabuf_len(dev->go_neg_conf), 0) >= + 0) { + dev->go_neg_conf_sent++; + return; + } + p2p_dbg(p2p, "Failed to re-send Action frame"); + + /* + * Continue with the assumption that the first attempt + * went through and just the ACK frame was lost. + */ + } + /* * It looks like the TX status for GO Negotiation Confirm is * often showing failure even when the peer has actually @@ -2927,13 +3590,11 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, * peer did indeed receive the frame, continue regardless of * the TX status. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Assume GO Negotiation Confirm TX was actually " - "received by the peer even though Ack was not " - "reported"); + p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); } - dev = p2p->go_neg_peer; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (dev == NULL) return; @@ -2948,16 +3609,20 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, enum p2p_pending_action_state state; int success; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR - " src=" MACSTR " bssid=" MACSTR " result=%d", + p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)", p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), - MAC2STR(bssid), result); + MAC2STR(bssid), result, p2p_state_txt(p2p->state)); success = result == P2P_SEND_ACTION_SUCCESS; state = p2p->pending_action_state; p2p->pending_action_state = P2P_NO_PENDING_ACTION; switch (state) { case P2P_NO_PENDING_ACTION: + if (p2p->send_action_in_progress) { + p2p->send_action_in_progress = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); @@ -2966,7 +3631,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_go_neg_resp_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: - p2p_go_neg_resp_failure_cb(p2p, success); + p2p_go_neg_resp_failure_cb(p2p, success, dst); break; case P2P_PENDING_GO_NEG_CONFIRM: p2p_go_neg_conf_cb(p2p, result); @@ -2977,6 +3642,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, case P2P_PENDING_PD: p2p_prov_disc_cb(p2p, success); break; + case P2P_PENDING_PD_RESPONSE: + p2p_prov_disc_resp_cb(p2p, success); + break; case P2P_PENDING_INVITATION_REQUEST: p2p_invitation_req_cb(p2p, success); break; @@ -2993,6 +3661,8 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_go_disc_req_cb(p2p, success); break; } + + p2p->after_scan_tx_in_progress = 0; } @@ -3000,23 +3670,18 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, unsigned int duration) { if (freq == p2p->pending_client_disc_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability remain-awake completed"); + p2p_dbg(p2p, "Client discoverability remain-awake completed"); p2p->pending_client_disc_freq = 0; return; } if (freq != p2p->pending_listen_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected listen callback for freq=%u " - "duration=%u (pending_listen_freq=%u)", + p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)", freq, duration, p2p->pending_listen_freq); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Starting Listen timeout(%u,%u) on freq=%u based on " - "callback", + p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", p2p->pending_listen_sec, p2p->pending_listen_usec, p2p->pending_listen_freq); p2p->in_listen = 1; @@ -3037,18 +3702,15 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen " - "state (freq=%u)", freq); + p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq); p2p->drv_in_listen = 0; if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on sending GO Negotiation " - "Request without getting response"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); + p2p_go_neg_failed(p2p, -1); return 0; } @@ -3065,9 +3727,7 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) * operation while in p2p_find. Avoid an attempt to * restart a scan here. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan " - "already in progress - do not try to start a " - "new one"); + p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one"); return 1; } if (p2p->pending_listen_freq) { @@ -3076,15 +3736,12 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) * offchannel operation for some reason. p2p_search() * will be started from internal timeout. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen " - "operation did not seem to start - delay " - "search phase to avoid busy loop"); + p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop"); p2p_set_timeout(p2p, 0, 100000); return 1; } if (p2p->search_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " - "search operation by %u ms", + p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p_set_timeout(p2p, p2p->search_delay / 1000, (p2p->search_delay % 1000) * 1000); @@ -3103,10 +3760,21 @@ static void p2p_timeout_connect(struct p2p_data *p2p) p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO " - "Negotiation Confirm timed out - assume GO " - "Negotiation failed"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); + p2p_go_neg_failed(p2p, -1); + return; + } + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) && + p2p->go_neg_peer->connect_reqs < 120) { + p2p_dbg(p2p, "Peer expected to wait our response - skip listen"); + p2p_connect_send(p2p, p2p->go_neg_peer); + return; + } + if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) { + p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)"); + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_set_timeout(p2p, 0, 30000); return; } p2p_set_state(p2p, P2P_CONNECT_LISTEN); @@ -3118,17 +3786,13 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p) { if (p2p->go_neg_peer) { if (p2p->drv_in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is " - "still in Listen state; wait for it to " - "complete"); + p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete"); return; } if (p2p->go_neg_peer->connect_reqs >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on sending GO Negotiation " - "Request without getting response"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); + p2p_go_neg_failed(p2p, -1); return; } @@ -3141,13 +3805,13 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p) static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) { - /* - * TODO: could remain constantly in Listen state for some time if there - * are no other concurrent uses for the radio. For now, go to listen - * state once per second to give other uses a chance to use the radio. - */ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); - p2p_set_timeout(p2p, 0, 500000); + + if (p2p->cfg->is_concurrent_session_active && + p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx)) + p2p_set_timeout(p2p, 0, 500000); + else + p2p_set_timeout(p2p, 0, 200000); } @@ -3156,23 +3820,11 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) struct p2p_device *dev = p2p->go_neg_peer; if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown GO Neg peer - stop GO Neg wait"); - return; - } - - dev->wait_count++; - if (dev->wait_count >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on waiting peer to become ready for GO " - "Negotiation"); - p2p_go_neg_failed(p2p, dev, -1); + p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Go to Listen state while waiting for the peer to become " - "ready for GO Negotiation"); + p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); p2p_listen_in_find(p2p, 0); } @@ -3180,11 +3832,9 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) static void p2p_timeout_sd_during_find(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery Query timeout"); + p2p_dbg(p2p, "Service Discovery Query timeout"); if (p2p->sd_peer) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; } p2p_continue_find(p2p); @@ -3193,8 +3843,7 @@ static void p2p_timeout_sd_during_find(struct p2p_data *p2p) static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request timeout"); + p2p_dbg(p2p, "Provision Discovery Request timeout"); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_continue_find(p2p); } @@ -3202,6 +3851,9 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) { + u32 adv_id = 0; + u8 *adv_mac = NULL; + p2p->pending_action_state = P2P_NO_PENDING_ACTION; /* @@ -3212,8 +3864,7 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) if (!p2p->user_initiated_pd) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: User initiated Provision Discovery Request timeout"); + p2p_dbg(p2p, "User initiated Provision Discovery Request timeout"); if (p2p->pd_retries) { p2p->pd_retries--; @@ -3231,12 +3882,18 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) for_join = 1; } + if (p2p->p2ps_prov) { + adv_id = p2p->p2ps_prov->adv_id; + adv_mac = p2p->p2ps_prov->adv_mac; + } + if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, p2p->pending_pd_devaddr, for_join ? P2P_PROV_DISC_TIMEOUT_JOIN : - P2P_PROV_DISC_TIMEOUT); + P2P_PROV_DISC_TIMEOUT, + adv_id, adv_mac, NULL); p2p_reset_pending_pd(p2p); } } @@ -3251,8 +3908,7 @@ static void p2p_timeout_invite(struct p2p_data *p2p) * Better remain on operating channel instead of listen channel * when running a group. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in " - "active GO role - wait on operating channel"); + p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel"); p2p_set_timeout(p2p, 0, 100000); return; } @@ -3265,14 +3921,15 @@ static void p2p_timeout_invite_listen(struct p2p_data *p2p) if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { p2p_set_state(p2p, P2P_INVITE); p2p_invite_send(p2p, p2p->invite_peer, - p2p->invite_go_dev_addr); + p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } else { if (p2p->invite_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request retry limit reached"); + p2p_dbg(p2p, "Invitation Request retry limit reached"); if (p2p->cfg->invitation_result) p2p->cfg->invitation_result( - p2p->cfg->cb_ctx, -1, NULL); + p2p->cfg->cb_ctx, -1, NULL, NULL, + p2p->invite_peer->info.p2p_device_addr, + 0, 0); } p2p_set_state(p2p, P2P_IDLE); } @@ -3283,10 +3940,13 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state)); p2p->in_listen = 0; + if (p2p->drv_in_listen) { + p2p_dbg(p2p, "Driver is still in listen state - stop it"); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + } switch (p2p->state) { case P2P_IDLE: @@ -3299,8 +3959,7 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); if (p2p->search_delay && !p2p->in_search_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " - "search operation by %u ms", + p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p->in_search_delay = 1; p2p_set_timeout(p2p, p2p->search_delay / 1000, @@ -3324,9 +3983,7 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) p2p_timeout_prov_disc_req(p2p); if (p2p->ext_listen_only) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Extended Listen Timing - Listen State " - "completed"); + p2p_dbg(p2p, "Extended Listen Timing - Listen State completed"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } @@ -3351,10 +4008,6 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) case P2P_INVITE_LISTEN: p2p_timeout_invite_listen(p2p); break; - case P2P_SEARCH_WHEN_READY: - break; - case P2P_CONTINUE_SEARCH_WHEN_READY: - break; } } @@ -3364,11 +4017,10 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) struct p2p_device *dev; dev = p2p_get_device(p2p, peer_addr); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject " - "connection attempts by peer " MACSTR, MAC2STR(peer_addr)); + p2p_dbg(p2p, "Local request to reject connection attempts by peer " + MACSTR, MAC2STR(peer_addr)); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR - " unknown", MAC2STR(peer_addr)); + p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr)); return -1; } dev->status = P2P_SC_FAIL_REJECTED_BY_USER; @@ -3388,6 +4040,10 @@ const char * p2p_wps_method_text(enum p2p_wps_method method) return "Keypad"; case WPS_PBC: return "PBC"; + case WPS_NFC: + return "NFC"; + case WPS_P2PS: + return "P2PS"; } return "??"; @@ -3438,7 +4094,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, struct p2p_device *dev; int res; char *pos, *end; - struct os_time now; + struct os_reltime now; if (info == NULL) return -1; @@ -3449,7 +4105,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, pos = buf; end = buf + buflen; - os_get_time(&now); + os_get_reltime(&now); res = os_snprintf(pos, end - pos, "age=%d\n" "listen_freq=%d\n" @@ -3464,9 +4120,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "country=%c%c\n" "oper_freq=%d\n" "req_config_methods=0x%x\n" - "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" - "wait_count=%u\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), dev->listen_freq, @@ -3487,13 +4142,12 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", dev->flags & P2P_DEV_NOT_YET_READY ? "[NOT_YET_READY]" : "", - dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "", - dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" : - "", dev->flags & P2P_DEV_PD_PEER_DISPLAY ? "[PD_PEER_DISPLAY]" : "", dev->flags & P2P_DEV_PD_PEER_KEYPAD ? "[PD_PEER_KEYPAD]" : "", + dev->flags & P2P_DEV_PD_PEER_P2PS ? + "[PD_PEER_P2PS]" : "", dev->flags & P2P_DEV_USER_REJECTED ? "[USER_REJECTED]" : "", dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? @@ -3511,9 +4165,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", dev->status, - dev->wait_count, dev->invitation_reqs); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -3523,7 +4176,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "ext_listen_interval=%u\n", dev->ext_listen_period, dev->ext_listen_interval); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -3533,7 +4186,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "oper_ssid=%s\n", wpa_ssid_txt(dev->oper_ssid, dev->oper_ssid_len)); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -3541,7 +4194,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, #ifdef CONFIG_WIFI_DISPLAY if (dev->info.wfd_subelems) { res = os_snprintf(pos, end - pos, "wfd_subelems="); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -3550,7 +4203,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, wpabuf_len(dev->info.wfd_subelems)); res = os_snprintf(pos, end - pos, "\n"); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -3569,12 +4222,10 @@ int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) { if (enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " - "discoverability enabled"); + p2p_dbg(p2p, "Client discoverability enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " - "discoverability disabled"); + p2p_dbg(p2p, "Client discoverability disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } } @@ -3623,9 +4274,9 @@ int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, { struct wpabuf *req; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to " - "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u " - "int1=%u dur2=%u int2=%u", + p2p_dbg(p2p, "Send Presence Request to GO " MACSTR + " (own interface " MACSTR ") freq=%u dur1=%u int1=%u " + "dur2=%u int2=%u", MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), freq, duration1, interval1, duration2, interval2); @@ -3638,8 +4289,7 @@ int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, go_interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); @@ -3685,8 +4335,7 @@ static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, u8 noa[50]; int noa_len; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - P2P Presence Request"); + p2p_dbg(p2p, "Received P2P Action - P2P Presence Request"); for (g = 0; g < p2p->num_groups; g++) { if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), @@ -3696,23 +4345,20 @@ static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, } } if (group == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore P2P Presence Request for unknown group " + p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group " MACSTR, MAC2STR(da)); return; } if (p2p_parse(data, len, &msg) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P Presence Request"); + p2p_dbg(p2p, "Failed to parse P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } parsed = 1; if (msg.noa == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No NoA attribute in P2P Presence Request"); + p2p_dbg(p2p, "No NoA attribute in P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } @@ -3736,8 +4382,7 @@ fail: p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, da, da, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } @@ -3748,33 +4393,32 @@ static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, { struct p2p_message msg; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - P2P Presence Response"); + p2p_dbg(p2p, "Received P2P Action - P2P Presence Response"); if (p2p_parse(data, len, &msg) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P Presence Response"); + p2p_dbg(p2p, "Failed to parse P2P Presence Response"); return; } if (msg.status == NULL || msg.noa == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status or NoA attribute in P2P Presence " - "Response"); + p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response"); p2p_parse_free(&msg); return; } + if (p2p->cfg->presence_resp) { + p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status, + msg.noa, msg.noa_len); + } + if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Presence Request was rejected: status %u", + p2p_dbg(p2p, "P2P Presence Request was rejected: status %u", *msg.status); p2p_parse_free(&msg); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Presence Request was accepted"); + p2p_dbg(p2p, "P2P Presence Request was accepted"); wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", msg.noa, msg.noa_len); /* TODO: process NoA */ @@ -3793,6 +4437,15 @@ static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) p2p_ext_listen_timeout, p2p, NULL); } + if ((p2p->cfg->is_p2p_in_progress && + p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) || + (p2p->pending_action_state == P2P_PENDING_PD && + p2p->pd_retries > 0)) { + p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)", + p2p_state_txt(p2p->state)); + return; + } + if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { /* * This should not really happen, but it looks like the Listen @@ -3800,25 +4453,20 @@ static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) * running at an inconvenient time. As a workaround, allow new * Extended Listen operation to be started. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous " - "Extended Listen operation had not been completed - " - "try again"); + p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } if (p2p->state != P2P_IDLE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended " - "Listen timeout in active state (%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state)); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout"); + p2p_dbg(p2p, "Extended Listen timeout"); p2p->ext_listen_only = 1; if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " - "Listen state for Extended Listen Timing"); + p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing"); p2p->ext_listen_only = 0; } } @@ -3829,25 +4477,22 @@ int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, { if (period > 65535 || interval > 65535 || period > interval || (period == 0 && interval > 0) || (period > 0 && interval == 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid Extended Listen Timing request: " - "period=%u interval=%u", period, interval); + p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u", + period, interval); return -1; } eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); if (interval == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Disabling Extended Listen Timing"); + p2p_dbg(p2p, "Disabling Extended Listen Timing"); p2p->ext_listen_period = 0; p2p->ext_listen_interval = 0; return 0; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Enabling Extended Listen Timing: period %u msec, " - "interval %u msec", period, interval); + p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec", + period, interval); p2p->ext_listen_period = period; p2p->ext_listen_interval = interval; p2p->ext_listen_interval_sec = interval / 1000; @@ -3872,11 +4517,12 @@ void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; - if (msg.minor_reason_code == NULL) + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Deauthentication notification BSSID " MACSTR + p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); @@ -3895,11 +4541,12 @@ void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; - if (msg.minor_reason_code == NULL) + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Disassociation notification BSSID " MACSTR + p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); @@ -3910,34 +4557,65 @@ void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) { if (enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " - "Device operations enabled"); + p2p_dbg(p2p, "Managed P2P Device operations enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " - "Device operations disabled"); + p2p_dbg(p2p, "Managed P2P Device operations disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; } } -int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) +int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, + u8 *op_channel) { - if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0) + return p2p_channel_random_social(&p2p->channels, op_class, op_channel); +} + + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, + u8 forced) +{ + if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: " - "reg_class %u channel %u", reg_class, channel); - p2p->cfg->reg_class = reg_class; - p2p->cfg->channel = channel; + /* + * Listen channel was set in configuration or set by control interface; + * cannot override it. + */ + if (p2p->cfg->channel_forced && forced == 0) { + p2p_dbg(p2p, + "Listen channel was previously configured - do not override based on optimization"); + return -1; + } + + p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", + reg_class, channel); + + if (p2p->state == P2P_IDLE) { + p2p->cfg->reg_class = reg_class; + p2p->cfg->channel = channel; + p2p->cfg->channel_forced = forced; + } else { + p2p_dbg(p2p, "Defer setting listen channel"); + p2p->pending_reg_class = reg_class; + p2p->pending_channel = channel; + p2p->pending_channel_forced = forced; + } return 0; } +u8 p2p_get_listen_channel(struct p2p_data *p2p) +{ + return p2p->cfg->channel; +} + + int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) { - wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len); + p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); if (postfix == NULL) { p2p->cfg->ssid_postfix_len = 0; return 0; @@ -3953,12 +4631,11 @@ int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel) { - if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel) - < 0) + if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: " - "reg_class %u channel %u", op_reg_class, op_channel); + p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u", + op_reg_class, op_channel); p2p->cfg->op_reg_class = op_reg_class; p2p->cfg->op_channel = op_channel; p2p->cfg->cfg_op_channel = cfg_op_channel; @@ -3988,6 +4665,31 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, } +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list) +{ + struct wpa_freq_range *tmp; + + if (list == NULL || list->num == 0) { + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = NULL; + p2p->no_go_freq.num = 0; + return 0; + } + + tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); + if (tmp == NULL) + return -1; + os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = tmp; + p2p->no_go_freq.num = list->num; + p2p_dbg(p2p, "Updated no GO chan list"); + + return 0; +} + + int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { @@ -4014,18 +4716,16 @@ void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) { os_memcpy(p2p->peer_filter, addr, ETH_ALEN); if (is_zero_ether_addr(p2p->peer_filter)) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer " - "filter"); + p2p_dbg(p2p, "Disable peer filter"); else - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer " - "filter for " MACSTR, MAC2STR(p2p->peer_filter)); + p2p_dbg(p2p, "Enable peer filter for " MACSTR, + MAC2STR(p2p->peer_filter)); } void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s", - enabled ? "enabled" : "disabled"); + p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled"); if (p2p->cross_connect == enabled) return; p2p->cross_connect = enabled; @@ -4046,16 +4746,22 @@ int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s", + p2p_dbg(p2p, "Intra BSS distribution %s", enabled ? "enabled" : "disabled"); p2p->cfg->p2p_intra_bss = enabled; } -void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan) +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list"); + p2p_dbg(p2p, "Update channel list"); os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + os_memcpy(&p2p->cfg->cli_channels, cli_chan, + sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); } @@ -4064,11 +4770,9 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, size_t len, unsigned int wait_time) { if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action " - "frame TX until p2p_scan completes"); + p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " - "previous pending Action frame TX"); + p2p_dbg(p2p, "Dropped previous pending Action frame TX"); os_free(p2p->after_scan_tx); } p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + @@ -4093,14 +4797,21 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d," - " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); + p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d", + freq_24, freq_5, freq_overall); p2p->best_freq_24 = freq_24; p2p->best_freq_5 = freq_5; p2p->best_freq_overall = freq_overall; } +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq) +{ + p2p_dbg(p2p, "Own frequency preference: %d MHz", freq); + p2p->own_freq_preference = freq; +} + + const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) { if (p2p == NULL || p2p->go_neg_peer == NULL) @@ -4129,7 +4840,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) dev = dl_list_first(&dev->list, struct p2p_device, list); - if (&dev->list == &p2p->devices) + if (!dev || &dev->list == &p2p->devices) return NULL; } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); } @@ -4141,7 +4852,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) dev = dl_list_first(&dev->list, struct p2p_device, list); - if (&dev->list == &p2p->devices) + if (!dev || &dev->list == &p2p->devices) return NULL; } } @@ -4154,8 +4865,7 @@ int p2p_in_progress(struct p2p_data *p2p) { if (p2p == NULL) return 0; - if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY || - p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) + if (p2p->state == P2P_SEARCH) return 2; return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; } @@ -4171,13 +4881,6 @@ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, } -void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay) -{ - if (p2p && p2p->search_delay < delay) - p2p->search_delay = delay; -} - - #ifdef CONFIG_WIFI_DISPLAY static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) @@ -4187,7 +4890,7 @@ static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) for (g = 0; g < p2p->num_groups; g++) { group = p2p->groups[g]; - p2p_group_update_ies(group); + p2p_group_force_beacon_update_ies(group); } } @@ -4312,9 +5015,322 @@ int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, p2p->min_disc_int = min_disc_int; p2p->max_disc_int = max_disc_int; p2p->max_disc_tu = max_disc_tu; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set discoverable interval: " - "min=%d max=%d max_tu=%d", min_disc_int, max_disc_int, - max_disc_tu); + p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d", + min_disc_int, max_disc_int, max_disc_tu); return 0; } + + +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf); +} + + +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf); +} + + +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf); +} + + +void p2p_loop_on_known_peers(struct p2p_data *p2p, + void (*peer_callback)(struct p2p_peer_info *peer, + void *user_data), + void *user_data) +{ + struct p2p_device *dev, *n; + + dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { + peer_callback(&dev->info, user_data); + } +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpabuf *buf; + u8 op_class, channel; + enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + op_class = p2p->cfg->reg_class; + channel = p2p->cfg->channel; + + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (p2p->num_groups > 0) { + int freq = p2p_group_get_freq(p2p->groups[0]); + role = P2P_GO_IN_A_GROUP; + if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) { + p2p_dbg(p2p, + "Unknown GO operating frequency %d MHz for NFC handover", + freq); + wpabuf_free(buf); + return NULL; + } + } else if (client_freq > 0) { + role = P2P_CLIENT_IN_A_GROUP; + if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) { + p2p_dbg(p2p, + "Unknown client operating frequency %d MHz for NFC handover", + client_freq); + wpabuf_free(buf); + return NULL; + } + } + + p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, + channel, role); + + if (p2p->num_groups > 0) { + /* Limit number of clients to avoid very long message */ + p2p_buf_add_group_info(p2p->groups[0], buf, 5); + p2p_group_buf_add_id(p2p->groups[0], buf); + } else if (client_freq > 0 && + go_dev_addr && !is_zero_ether_addr(go_dev_addr) && + ssid && ssid_len > 0) { + /* + * Add the optional P2P Group ID to indicate in which group this + * device is a P2P Client. + */ + p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len); + } + + return buf; +} + + +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) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + 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) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params) +{ + struct p2p_message msg; + struct p2p_device *dev; + const u8 *p2p_dev_addr; + int freq; + enum p2p_role_indication role; + + params->next_step = NO_ACTION; + + if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, + params->p2p_attr, params->p2p_len, &msg)) { + p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.oob_dev_password) { + os_memcpy(params->oob_dev_pw, msg.oob_dev_password, + msg.oob_dev_password_len); + params->oob_dev_pw_len = msg.oob_dev_password_len; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + params->peer = &dev->info; + + os_get_reltime(&dev->last_seen); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + p2p_copy_wps_info(p2p, dev, 0, &msg); + + if (!msg.oob_go_neg_channel) { + p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + return -1; + } + + if (msg.oob_go_neg_channel[3] == 0 && + msg.oob_go_neg_channel[4] == 0) + freq = 0; + else + freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], + msg.oob_go_neg_channel[4]); + if (freq < 0) { + p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + return -1; + } + role = msg.oob_go_neg_channel[5]; + + if (role == P2P_GO_IN_A_GROUP) { + p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq); + params->go_freq = freq; + } else if (role == P2P_CLIENT_IN_A_GROUP) { + p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz", + freq); + params->go_freq = freq; + } else + p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + + if (!params->sel && role != P2P_GO_IN_A_GROUP) { + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Own listen channel not known"); + return -1; + } + p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + } + + if (msg.group_id) { + os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN); + params->go_ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN, + params->go_ssid_len); + } + + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + p2p_parse_free(&msg); + return 0; + } + + if (!(dev->flags & P2P_DEV_REPORTED)) { + p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + p2p_parse_free(&msg); + + if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0) + params->next_step = BOTH_GO; + else if (role == P2P_GO_IN_A_GROUP) + params->next_step = JOIN_GROUP; + else if (role == P2P_CLIENT_IN_A_GROUP) { + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + params->next_step = PEER_CLIENT; + } else if (p2p->num_groups > 0) + params->next_step = AUTH_JOIN; + else if (params->sel) + params->next_step = INIT_GO_NEG; + else + params->next_step = RESP_GO_NEG; + + return 0; +} + + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr) +{ + + p2p->authorized_oob_dev_pw_id = dev_pw_id; + if (dev_pw_id == 0) { + p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover"); + return; + } + + p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover", + dev_pw_id); + + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); +} + +#endif /* CONFIG_WPS_NFC */ + + +int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len) +{ + if (len < 8 || len > 63) + return -1; + p2p->cfg->passphrase_len = len; + return 0; +} + + +void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem) +{ + p2p->vendor_elem = vendor_elem; +} + + +void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + p2p_dbg(p2p, + "Timeout on waiting peer to become ready for GO Negotiation"); + p2p_go_neg_failed(p2p, -1); +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 2d8b2c25750b..2402db6a7eb9 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -9,6 +9,18 @@ #ifndef P2P_H #define P2P_H +#include "wps/wps_defs.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 + /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ @@ -50,7 +62,8 @@ struct p2p_channels { }; enum p2p_wps_method { - WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC, + WPS_P2PS }; /** @@ -77,6 +90,8 @@ struct p2p_go_neg_results { int ht40; + int vht; + /** * ssid - SSID of the group */ @@ -138,11 +153,99 @@ struct p2p_go_neg_results { unsigned int peer_config_timeout; }; +struct p2ps_provision { + /** + * 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]; + + /** + * 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]; + + /** + * 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 }; @@ -226,6 +329,19 @@ struct p2p_peer_info { * 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 { @@ -233,6 +349,7 @@ enum p2p_prov_disc_status { P2P_PROV_DISC_TIMEOUT, P2P_PROV_DISC_REJECTED, P2P_PROV_DISC_TIMEOUT_JOIN, + P2P_PROV_DISC_INFO_UNAVAILABLE, }; struct p2p_channel { @@ -262,6 +379,12 @@ struct p2p_config { */ 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 */ @@ -286,6 +409,20 @@ struct p2p_config { */ 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 */ @@ -371,15 +508,26 @@ struct p2p_config { unsigned int max_listen; /** - * msg_ctx - Context to use with wpa_msg() calls + * passphrase_len - Passphrase length (8..63) + * + * This parameter controls the length of the random passphrase that is + * generated at the GO. */ - void *msg_ctx; + 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 */ @@ -398,7 +546,8 @@ struct p2p_config { * 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. + * 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. * @@ -544,6 +693,12 @@ struct p2p_config { */ 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 @@ -656,6 +811,9 @@ struct p2p_config { * @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. @@ -664,7 +822,9 @@ struct p2p_config { * 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); + 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 @@ -680,6 +840,9 @@ struct p2p_config { * @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 @@ -700,7 +863,9 @@ struct p2p_config { 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); + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id); /** * invitation_received - Callback on Invitation Request RX @@ -729,6 +894,11 @@ struct p2p_config { * @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 @@ -736,7 +906,9 @@ struct p2p_config { * (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); + 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 @@ -746,6 +918,111 @@ struct p2p_config { * 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 + * @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 intended GO P2P Device Address + * @ret_ssid: Buffer for returning group SSID + * @ret_ssid_len: Buffer for returning length of @ssid + * 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); + + /** + * 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); + + /** + * 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); }; @@ -852,12 +1129,26 @@ enum p2p_discovery_type { * 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); + 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) @@ -888,6 +1179,12 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); */ 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() @@ -914,7 +1211,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, 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); + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) @@ -942,7 +1239,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, 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); + unsigned int pref_freq, u16 oob_pw_id); /** * p2p_reject - Reject peer device (explicitly block connection attempts) @@ -956,6 +1253,7 @@ 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) @@ -971,7 +1269,8 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); * indicated with the p2p_config::prov_disc_resp() callback. */ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, - u16 config_methods, int join, int force_freq, + struct p2ps_provision *p2ps_prov, u16 config_methods, + int join, int force_freq, int user_initiated_pd); /** @@ -1042,12 +1341,16 @@ enum p2p_invite_role { * @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); + int persistent_group, unsigned int pref_freq, int dev_pw_id); /** * p2p_presence_req - Request GO presence @@ -1183,7 +1486,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, * @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 - * @age: Age of the scan result in milliseconds + * @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 @@ -1205,7 +1508,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, * 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, - unsigned int age, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len); /** @@ -1311,6 +1614,11 @@ struct p2p_group_config { */ size_t ssid_len; + /** + * freq - Operating channel of the group + */ + int freq; + /** * cb_ctx - Context to use with callback functions */ @@ -1587,7 +1895,24 @@ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); */ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); -int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); +/** + * 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); @@ -1614,6 +1939,12 @@ int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); */ 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() @@ -1622,7 +1953,34 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); -void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); +/** + * 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 @@ -1634,6 +1992,17 @@ void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); 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); /** @@ -1643,12 +2012,19 @@ const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); */ 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 Interface Address for each call and %NULL for no more members + * 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); @@ -1669,6 +2045,26 @@ const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); */ 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() @@ -1719,18 +2115,21 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan); /** - * p2p_in_progress - Check whether a P2P operation is progress + * p2p_set_no_go_freq - Set no GO channel ranges * @p2p: P2P module context from p2p_init() - * Returns: 0 if P2P module is idle or 1 if an operation is in progress + * @list: Channel ranges or %NULL to remove restriction + * Returns: 0 on success, -1 on failure */ -int p2p_in_progress(struct p2p_data *p2p); +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list); /** - * p2p_other_scan_completed - Notify completion of non-P2P scan + * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() - * Returns: 0 if P2P module is idle or 1 if an operation was started + * 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_other_scan_completed(struct p2p_data *p2p); +int p2p_in_progress(struct p2p_data *p2p); const char * p2p_wps_method_text(enum p2p_wps_method method); @@ -1743,8 +2142,6 @@ const char * p2p_wps_method_text(enum p2p_wps_method method); void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, u8 client_timeout); -void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay); - 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); @@ -1780,4 +2177,71 @@ struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); 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[32]; + 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); +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id); +struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 5838d35e977e..92c920662edb 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -17,8 +17,7 @@ void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) { wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpabuf_put_u8(buf, subtype); /* OUI Subtype */ wpabuf_put_u8(buf, dialog_token); @@ -31,8 +30,7 @@ void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, { wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpabuf_put_u8(buf, subtype); /* OUI Subtype */ wpabuf_put_u8(buf, dialog_token); @@ -47,8 +45,7 @@ u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) /* P2P IE header */ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); /* IE length to be filled */ - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpa_printf(MSG_DEBUG, "P2P: * P2P IE header"); return len; } @@ -167,15 +164,18 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, if (peer->wps_method == WPS_PBC) methods |= WPS_CONFIG_PUSHBUTTON; else if (peer->wps_method == WPS_PIN_DISPLAY || - peer->wps_method == WPS_PIN_KEYPAD) + peer->wps_method == WPS_PIN_KEYPAD) { methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + methods |= WPS_CONFIG_P2PS; + } } else if (p2p->cfg->config_methods) { methods |= p2p->cfg->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | - WPS_CONFIG_KEYPAD); + WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS); } else { methods |= WPS_CONFIG_PUSHBUTTON; methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + methods |= WPS_CONFIG_P2PS; } wpabuf_put_be16(buf, methods); @@ -258,6 +258,7 @@ void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, wpabuf_put_data(buf, ssid, ssid_len); wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, MAC2STR(dev_addr)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len); } @@ -327,13 +328,282 @@ void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p) } -static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, - const char *val) +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role) +{ + /* OOB Group Owner Negotiation Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL); + wpabuf_put_le16(buf, 6); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, oper_class); /* Operating Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpabuf_put_u8(buf, (u8) role); /* Role indication */ + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating " + "Class %u Channel %u Role %d", + oper_class, channel, role); +} + + +void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p) +{ + if (!p2p) + return; + + /* Service Hash */ + wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH); + wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN); + wpabuf_put_data(buf, p2p->query_hash, + p2p->p2ps_seek_count * P2PS_HASH_LEN); + wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash", + p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN); +} + + +void p2p_buf_add_session_info(struct wpabuf *buf, const char *info) +{ + size_t info_len = 0; + + if (info && info[0]) + info_len = os_strlen(info); + + /* Session Information Data Info */ + wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA); + wpabuf_put_le16(buf, (u16) info_len); + + if (info) { + wpabuf_put_data(buf, info, info_len); + wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info); + } +} + + +void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap) +{ + /* Connection Capability Info */ + wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, connection_cap); + wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x", + connection_cap); +} + + +void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac) +{ + if (!buf || !mac) + return; + + /* Advertisement ID Info */ + wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID); + wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN)); + wpabuf_put_le32(buf, id); + wpabuf_put_data(buf, mac, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR, + id, MAC2STR(mac)); +} + + +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 hash_count, const u8 *hash, + struct p2ps_advertisement *adv_list) +{ + struct p2ps_advertisement *adv; + struct wpabuf *tmp_buf; + u8 *tag_len = NULL, *ie_len = NULL; + size_t svc_len = 0, remaining = 0, total_len = 0; + + if (!adv_list || !hash) + return; + + /* Allocate temp buffer, allowing for overflow of 1 instance */ + tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN); + if (!tmp_buf) + return; + + for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN; + adv = adv->next) { + u8 count = hash_count; + const u8 *test = hash; + + while (count--) { + /* Check for wildcard */ + if (os_memcmp(test, p2p->wild_card_hash, + P2PS_HASH_LEN) == 0) { + total_len = MAX_SVC_ADV_LEN + 1; + goto wild_hash; + } + + if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0) + goto hash_match; + + test += P2PS_HASH_LEN; + } + + /* No matches found - Skip this Adv Instance */ + continue; + +hash_match: + if (!tag_len) { + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + remaining = 255 - 4; + if (!ie_len) { + wpabuf_put_u8(tmp_buf, + P2P_ATTR_ADVERTISED_SERVICE); + ie_len = wpabuf_put(tmp_buf, sizeof(u16)); + remaining -= (sizeof(u8) + sizeof(u16)); + } + } + + svc_len = os_strlen(adv->svc_name); + + if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) { + /* Can't fit... return wildcard */ + total_len = MAX_SVC_ADV_LEN + 1; + break; + } + + if (remaining <= (sizeof(adv->id) + + sizeof(adv->config_methods))) { + size_t front = remaining; + size_t back = (sizeof(adv->id) + + sizeof(adv->config_methods)) - front; + u8 holder[sizeof(adv->id) + + sizeof(adv->config_methods)]; + + /* This works even if front or back == 0 */ + WPA_PUT_LE32(holder, adv->id); + WPA_PUT_BE16(&holder[sizeof(adv->id)], + adv->config_methods); + wpabuf_put_data(tmp_buf, holder, front); + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + wpabuf_put_data(tmp_buf, &holder[front], back); + remaining = 255 - (sizeof(adv->id) + + sizeof(adv->config_methods)) - back; + } else { + wpabuf_put_le32(tmp_buf, adv->id); + wpabuf_put_be16(tmp_buf, adv->config_methods); + remaining -= (sizeof(adv->id) + + sizeof(adv->config_methods)); + } + + /* We are guaranteed at least one byte for svc_len */ + wpabuf_put_u8(tmp_buf, svc_len); + remaining -= sizeof(u8); + + if (remaining < svc_len) { + size_t front = remaining; + size_t back = svc_len - front; + + wpabuf_put_data(tmp_buf, adv->svc_name, front); + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + + /* In rare cases, we must split across 3 attributes */ + if (back > 255 - 4) { + wpabuf_put_data(tmp_buf, + &adv->svc_name[front], 255 - 4); + back -= 255 - 4; + front += 255 - 4; + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + tag_len = p2p_buf_add_ie_hdr(tmp_buf); + } + + wpabuf_put_data(tmp_buf, &adv->svc_name[front], back); + remaining = 255 - 4 - back; + } else { + wpabuf_put_data(tmp_buf, adv->svc_name, svc_len); + remaining -= svc_len; + } + + /* adv_id config_methods svc_string */ + total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len; + } + + if (tag_len) + p2p_buf_update_ie_hdr(tmp_buf, tag_len); + + if (ie_len) + WPA_PUT_LE16(ie_len, (u16) total_len); + +wild_hash: + /* If all fit, return matching instances, otherwise the wildcard */ + if (total_len <= MAX_SVC_ADV_LEN) { + wpabuf_put_buf(buf, tmp_buf); + } else { + char *wild_card = P2PS_WILD_HASH_STR; + u8 wild_len; + + /* Insert wildcard instance */ + tag_len = p2p_buf_add_ie_hdr(buf); + wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE); + ie_len = wpabuf_put(buf, sizeof(u16)); + + wild_len = (u8) os_strlen(wild_card); + wpabuf_put_le32(buf, 0); + wpabuf_put_be16(buf, 0); + wpabuf_put_u8(buf, wild_len); + wpabuf_put_data(buf, wild_card, wild_len); + + WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len); + p2p_buf_update_ie_hdr(buf, tag_len); + } + + wpabuf_free(tmp_buf); +} + + +void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac) +{ + if (!buf || !mac) + return; + + /* Session ID Info */ + wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID); + wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN)); + wpabuf_put_le32(buf, id); + wpabuf_put_data(buf, mac, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR, + id, MAC2STR(mac)); +} + + +void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask) +{ + if (!buf || !len || !mask) + return; + + /* Feature Capability */ + wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY); + wpabuf_put_le16(buf, len); + wpabuf_put_data(buf, mask, len); + wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len); +} + + +void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len) +{ + /* P2P Group ID */ + wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP); + wpabuf_put_le16(buf, ETH_ALEN + ssid_len); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpabuf_put_data(buf, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, + MAC2STR(dev_addr)); +} + + +static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, + const char *val) { size_t len; - wpabuf_put_be16(buf, attr); len = val ? os_strlen(val) : 0; + if (wpabuf_tailroom(buf) < 4 + len) + return -1; + wpabuf_put_be16(buf, attr); #ifndef CONFIG_WPS_STRICT if (len == 0) { /* @@ -341,36 +611,46 @@ static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, * attributes. As a workaround, send a space character if the * device attribute string is empty. */ + if (wpabuf_tailroom(buf) < 3) + return -1; wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, ' '); - return; + return 0; } #endif /* CONFIG_WPS_STRICT */ wpabuf_put_be16(buf, len); if (val) wpabuf_put_data(buf, val, len); + return 0; } -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, - int all_attr) +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr) { u8 *len; int i; + if (wpabuf_tailroom(buf) < 6) + return -1; wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); - wps_build_version(buf); + if (wps_build_version(buf) < 0) + return -1; if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; wpabuf_put_be16(buf, ATTR_WPS_STATE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); } if (pw_id >= 0) { + if (wpabuf_tailroom(buf) < 6) + return -1; /* Device Password ID */ wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); wpabuf_put_be16(buf, 2); @@ -380,33 +660,47 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, } if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); - wps_build_uuid_e(buf, p2p->cfg->uuid); - p2p_add_wps_string(buf, ATTR_MANUFACTURER, - p2p->cfg->manufacturer); - p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name); - p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, - p2p->cfg->model_number); - p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, - p2p->cfg->serial_number); - + if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 || + p2p_add_wps_string(buf, ATTR_MANUFACTURER, + p2p->cfg->manufacturer) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NAME, + p2p->cfg->model_name) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, + p2p->cfg->model_number) < 0 || + p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, + p2p->cfg->serial_number) < 0) + return -1; + + if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN) + return -1; wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); - p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name); + if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name) + < 0) + return -1; + if (wpabuf_tailroom(buf) < 6) + return -1; wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); wpabuf_put_be16(buf, 2); wpabuf_put_be16(buf, p2p->cfg->config_methods); } - wps_build_wfa_ext(buf, 0, NULL, 0); + if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { + if (wpabuf_tailroom(buf) < + 4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types) + return -1; wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types); @@ -428,4 +722,6 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, } p2p_buf_update_ie_hdr(buf, len); + + return 0; } diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c index c976b7c6d62b..86bae1a2c0db 100644 --- a/src/p2p/p2p_dev_disc.c +++ b/src/p2p/p2p_dev_disc.c @@ -42,8 +42,7 @@ static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Request TX callback: success=%d", + p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d", success); if (!success) { @@ -56,9 +55,7 @@ void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO acknowledged Device Discoverability Request - wait " - "for response"); + p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response"); /* * TODO: is the remain-on-channel from Action frame TX long enough for * most cases or should we try to increase its duration and/or start @@ -71,12 +68,11 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) { struct p2p_device *go; struct wpabuf *req; + unsigned int wait_time; go = p2p_get_device(p2p, dev->member_in_go_dev); if (go == NULL || dev->oper_freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Could not find peer entry for GO and frequency " - "to send Device Discoverability Request"); + p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request"); return -1; } @@ -84,8 +80,7 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) if (req == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Device Discoverability Request to GO " MACSTR + p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR " for client " MACSTR, MAC2STR(go->info.p2p_device_addr), MAC2STR(dev->info.p2p_device_addr)); @@ -94,11 +89,13 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, ETH_ALEN); p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; + wait_time = 1000; + if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen) + wait_time = p2p->cfg->max_listen; if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, p2p->cfg->dev_addr, go->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 1000) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), wait_time) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); /* TODO: how to recover from failure? */ return -1; @@ -131,8 +128,7 @@ static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Response TX callback: success=%d", + p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } @@ -147,8 +143,7 @@ static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Device Discoverability Response to " MACSTR + p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR " (status %u freq %d)", MAC2STR(addr), status, freq); @@ -156,8 +151,7 @@ static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -170,17 +164,14 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, struct p2p_message msg; size_t g; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Device Discoverability Request from " MACSTR + p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (msg.dialog_token == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid Dialog Token 0 (must be nonzero) in " - "Device Discoverability Request"); + p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); @@ -188,9 +179,7 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, } if (msg.device_id == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Device ID attribute missing from Device " - "Discoverability Request"); + p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); @@ -200,9 +189,7 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, for (g = 0; g < p2p->num_groups; g++) { if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, rx_freq) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled " - "GO Discoverability Request for the target " - "device"); + p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device"); /* * P2P group code will use a callback to indicate TX * status, so that we can reply to the request once the @@ -217,9 +204,7 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, } } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client " - "was not found in any group or did not support client " - "discoverability"); + p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); p2p_parse_free(&msg); @@ -233,15 +218,13 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *go; u8 status; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Device Discoverability Response from " MACSTR, + p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR, MAC2STR(sa)); go = p2p->pending_client_disc_go; if (go == NULL || os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected " - "Device Discoverability Response"); + p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response"); return; } @@ -254,9 +237,7 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, } if (msg.dialog_token != go->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device " - "Discoverability Response with unexpected dialog " - "token %u (expected %u)", + p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)", msg.dialog_token, go->dialog_token); p2p_parse_free(&msg); return; @@ -265,17 +246,14 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, status = *msg.status; p2p_parse_free(&msg); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Response status %u", status); + p2p_dbg(p2p, "Device Discoverability Response status %u", status); if (p2p->go_neg_peer == NULL || os_memcmp(p2p->pending_client_disc_addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || os_memcmp(p2p->go_neg_peer->member_in_go_dev, go->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending " - "operation with the client discoverability peer " - "anymore"); + p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore"); return; } @@ -284,8 +262,7 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, * Peer is expected to be awake for at least 100 TU; try to * connect immediately. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability request succeeded"); + p2p_dbg(p2p, "Client discoverability request succeeded"); if (p2p->state == P2P_CONNECT) { /* * Change state to force the timeout to start in @@ -301,8 +278,7 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, * Client discoverability request failed; try to connect from * timeout. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability request failed"); + p2p_dbg(p2p, "Client discoverability request failed"); p2p_set_timeout(p2p, 0, 500000); } @@ -311,14 +287,12 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Discoverability Request TX callback: success=%d", + p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->pending_dev_disc_dialog_token == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device " - "Discoverability Request"); + p2p_dbg(p2p, "No pending Device Discoverability Request"); return; } @@ -338,9 +312,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, unsigned int tu; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Discoverability Request - remain awake for " - "100 TU"); + p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) @@ -351,9 +323,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, tu = 100; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode for client " - "discoverability"); + p2p_dbg(p2p, "Failed to start listen mode for client discoverability"); } wpabuf_free(ies); } diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 2fdc47fc5d0f..98abf9d2293e 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -9,7 +9,9 @@ #include "includes.h" #include "common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "p2p_i.h" #include "p2p.h" @@ -49,8 +51,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, os_memcpy(dev->country, pos, 3); wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Mismatching country (ours=%c%c peer's=%c%c)", + p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)", p2p->cfg->country[0], p2p->cfg->country[1], pos[0], pos[1]); return -1; @@ -61,8 +62,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; if (pos + 1 + pos[0] > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Invalid peer Channel List"); + p2p_info(p2p, "Invalid peer Channel List"); return -1; } channels = *pos++; @@ -76,14 +76,12 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, } p2p_channels_intersect(own, &dev->channels, &intersection); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d " - "peer reg_classes %d intersection reg_classes %d", + p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d", (int) own->reg_classes, (int) dev->channels.reg_classes, (int) intersection.reg_classes); if (intersection.reg_classes == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: No common channels found"); + p2p_info(p2p, "No common channels found"); return -1; } return 0; @@ -107,6 +105,10 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) return DEV_PW_USER_SPECIFIED; case WPS_PBC: return DEV_PW_PUSHBUTTON; + case WPS_NFC: + return DEV_PW_NFC_CONNECTION_HANDOVER; + case WPS_P2PS: + return DEV_PW_P2PS_DEFAULT; default: return DEV_PW_DEFAULT; } @@ -122,6 +124,10 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) return "Keypad"; case WPS_PBC: return "PBC"; + case WPS_NFC: + return "NFC"; + case WPS_P2PS: + return "P2PS"; default: return "??"; } @@ -135,19 +141,20 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, u8 *len; u8 group_capab; size_t extra = 0; + u16 pw_id; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; - peer->dialog_token++; - if (peer->dialog_token == 0) - peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); @@ -164,9 +171,7 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | - p2p->next_tie_breaker); - p2p->next_tie_breaker = !p2p->next_tie_breaker; + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); @@ -181,13 +186,23 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ - p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); + pw_id = p2p_wps_method_pw_id(peer->wps_method); + if (peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); + wpabuf_free(buf); + return NULL; + } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + return buf; } @@ -199,8 +214,7 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { u16 config_method; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use PD-before-GO-Neg workaround for " MACSTR, + p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR, MAC2STR(dev->info.p2p_device_addr)); if (dev->wps_method == WPS_PIN_DISPLAY) config_method = WPS_CONFIG_KEYPAD; @@ -208,17 +222,20 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) config_method = WPS_CONFIG_DISPLAY; else if (dev->wps_method == WPS_PBC) config_method = WPS_CONFIG_PUSHBUTTON; + else if (dev->wps_method == WPS_P2PS) + config_method = WPS_CONFIG_P2PS; else return -1; return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, - config_method, 0, 0, 1); + NULL, config_method, 0, 0, 1); } freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (dev->oob_go_neg_freq > 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send GO Negotiation Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send GO Negotiation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -226,18 +243,17 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) req = p2p_build_go_neg_req(p2p, dev); if (req == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Request"); + p2p_dbg(p2p, "Sending GO Negotiation Request"); p2p_set_state(p2p, P2P_CONNECT); p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; p2p->go_neg_peer = dev; + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->connect_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); } else @@ -258,15 +274,18 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, u8 *len; u8 group_capab; size_t extra = 0; + u16 pw_id; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Building GO Negotiation Response"); + p2p_dbg(p2p, "Building GO Negotiation Response"); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -294,8 +313,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); if (peer && peer->go_state == REMOTE_GO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating " - "Channel attribute"); + p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, @@ -322,15 +340,22 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ - p2p_build_wps_ie(p2p, buf, - p2p_wps_method_pw_id(peer ? peer->wps_method : - WPS_NOT_READY), 0); + pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); + if (peer && peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); + wpabuf_free(buf); + return NULL; + } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); return buf; } @@ -354,16 +379,41 @@ void p2p_reselect_channel(struct p2p_data *p2p, int freq; u8 op_reg_class, op_channel; unsigned int i; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + if (p2p->own_freq_preference > 0 && + p2p_freq_to_channel(p2p->own_freq_preference, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + if (p2p->best_freq_overall > 0 && + p2p_freq_to_channel(p2p->best_freq_overall, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } /* First, try to pick the best channel from another band */ - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class, - p2p->op_channel); + freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_5, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz " - "channel (reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; @@ -371,11 +421,12 @@ void p2p_reselect_channel(struct p2p_data *p2p, } if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_24, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz " - "channel (reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; @@ -389,27 +440,34 @@ void p2p_reselect_channel(struct p2p_data *p2p, p2p->cfg->pref_chan[i].chan)) { p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; p2p->op_channel = p2p->cfg->pref_chan[i].chan; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick " - "highest preferred chnnel (op_class %u " - "channel %u) from intersection", + p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } } + /* Try a channel where we might be able to use VHT */ + if (p2p_channel_select(intersection, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + /* Try a channel where we might be able to use HT40 */ - for (i = 0; i < intersection->reg_classes; i++) { - struct p2p_reg_class *c = &intersection->reg_class[i]; - if (c->reg_class == 116 || c->reg_class == 117 || - c->reg_class == 126 || c->reg_class == 127) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Pick possible HT40 channel (reg_class " - "%u channel %u) from intersection", - c->reg_class, c->channel[0]); - p2p->op_reg_class = c->reg_class; - p2p->op_channel = c->channel[0]; - return; - } + if (p2p_channel_select(intersection, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* Prefer a 5 GHz channel */ + if (p2p_channel_select(intersection, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; } /* @@ -419,9 +477,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, */ if (p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Using original operating class and channel " - "(op_class %u channel %u) from intersection", + p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } @@ -431,55 +487,48 @@ void p2p_reselect_channel(struct p2p_data *p2p, * no better options seems to be available. */ cl = &intersection->reg_class[0]; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel " - "(reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection", cl->reg_class, cl->channel[0]); p2p->op_reg_class = cl->reg_class; p2p->op_channel = cl->channel[0]; } -static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, - u8 *status) +int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status) { - struct p2p_channels intersection; - size_t i; - - p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); + struct p2p_channels tmp, intersection; + + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &dev->channels); + p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp); + p2p_channels_dump(p2p, "intersection", &tmp); + p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp); + p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection); + p2p_channels_dump(p2p, "intersection with local channel list", + &intersection); if (intersection.reg_classes == 0 || intersection.reg_class[0].channels == 0) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); return -1; } - for (i = 0; i < intersection.reg_classes; i++) { - struct p2p_reg_class *c; - c = &intersection.reg_class[i]; - wpa_printf(MSG_DEBUG, "P2P: reg_class %u", c->reg_class); - wpa_hexdump(MSG_DEBUG, "P2P: channels", - c->channel, c->channels); - } - if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { if (dev->flags & P2P_DEV_FORCE_FREQ) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer does " - "not support the forced channel"); + p2p_dbg(p2p, "Peer does not support the forced channel"); return -1; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " - "channel (op_class %u channel %u) not acceptable to " - "the peer", p2p->op_reg_class, p2p->op_channel); + p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer", + p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && !p2p->cfg->cfg_op_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Try to optimize " - "channel selection with peer information received; " - "previously selected op_class %u channel %u", + p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } @@ -503,17 +552,14 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, int tie_breaker = 0; int freq; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Request from " MACSTR - "(freq=%d)", MAC2STR(sa), rx_freq); + p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", + MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (!msg.capability) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Capability attribute missing from GO " - "Negotiation Request"); + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ @@ -522,53 +568,42 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, if (msg.go_intent) tie_breaker = *msg.go_intent & 0x01; else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory GO Intent attribute missing from GO " - "Negotiation Request"); + p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Configuration Timeout attribute " - "missing from GO Negotiation Request"); + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.listen_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen Channel attribute received"); + p2p_dbg(p2p, "No Listen Channel attribute received"); goto fail; } if (!msg.operating_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Operating Channel attribute received"); + p2p_dbg(p2p, "No Operating Channel attribute received"); goto fail; } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Channel List attribute received"); + p2p_dbg(p2p, "No Channel List attribute received"); goto fail; } if (!msg.intended_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Intended P2P Interface Address attribute " - "received"); + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); goto fail; } if (!msg.p2p_device_info) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No P2P Device Info attribute received"); + p2p_dbg(p2p, "No P2P Device Info attribute received"); goto fail; } if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected GO Negotiation Request SA=" MACSTR + p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR " != dev_addr=" MACSTR, MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); goto fail; @@ -577,133 +612,177 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, dev = p2p_get_device(p2p, sa); if (msg.status && *msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Status attribute (%d) in GO " - "Negotiation Request", *msg.status); + p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request", + *msg.status); + if (dev && p2p->go_neg_peer == dev && + *msg.status == P2P_SC_FAIL_REJECTED_BY_USER) { + /* + * This mechanism for using Status attribute in GO + * Negotiation Request is not compliant with the P2P + * specification, but some deployed devices use it to + * indicate rejection of GO Negotiation in a case where + * they have sent out GO Negotiation Response with + * status 1. The P2P specification explicitly disallows + * this. To avoid unnecessary interoperability issues + * and extra frames, mark the pending negotiation as + * failed and do not reply to this GO Negotiation + * Request frame. + */ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_go_neg_failed(p2p, *msg.status); + p2p_parse_free(&msg); + return; + } goto fail; } if (dev == NULL) dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); - else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) || + !(dev->flags & P2P_DEV_REPORTED)) p2p_add_dev_info(p2p, sa, dev, &msg); + else if (!dev->listen_freq && !dev->oper_freq) { + /* + * This may happen if the peer entry was added based on PD + * Request and no Probe Request/Response frame has been received + * from this peer (or that information has timed out). + */ + p2p_dbg(p2p, "Update peer " MACSTR + " based on GO Neg Req since listen/oper freq not known", + MAC2STR(dev->info.p2p_device_addr)); + p2p_add_dev_info(p2p, sa, dev, &msg); + } + + if (p2p->go_neg_peer && p2p->go_neg_peer == dev) + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); + if (dev && dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: User has rejected this peer"); + p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; - } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + } else if (dev == NULL || + (dev->wps_method == WPS_NOT_READY && + (p2p->authorized_oob_dev_pw_id == 0 || + p2p->authorized_oob_dev_pw_id != + msg.dev_password_id))) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - if (dev) - dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, msg.dev_password_id); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Already in Group Formation with another peer"); + p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } else { int go; if (!p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting " - "GO Negotiation with previously authorized " - "peer"); + p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer"); if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use default channel settings"); + p2p_dbg(p2p, "Use default channel settings"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use previously configured " - "forced channel settings"); + p2p_dbg(p2p, "Use previously configured forced channel settings"); } } dev->flags &= ~P2P_DEV_NOT_YET_READY; if (!msg.go_intent) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No GO Intent attribute received"); + p2p_dbg(p2p, "No GO Intent attribute received"); goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid GO Intent value (%u) received", + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); goto fail; } if (dev->go_neg_req_sent && os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not reply since peer has higher " - "address and GO Neg Request already sent"); + p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); p2p_parse_free(&msg); return; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Incompatible GO Intent"); + p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_BOTH_GO_INTENT_15; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: PIN from peer Display"); + p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_USER_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer entered PIN on Keypad"); + p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_PUSHBUTTON: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer using pushbutton"); + p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_P2PS_DEFAULT: + p2p_dbg(p2p, "Peer using P2PS pin"); + if (dev->wps_method != WPS_P2PS) { + p2p_dbg(p2p, + "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported Device Password ID %d", + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str( + dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + } +#ifdef CONFIG_WPS_NFC + if (p2p->authorized_oob_dev_pw_id && + msg.dev_password_id == + p2p->authorized_oob_dev_pw_id) { + p2p_dbg(p2p, "Using static handover with our device password from NFC Tag"); + dev->wps_method = WPS_NFC; + dev->oob_pw_id = p2p->authorized_oob_dev_pw_id; + break; + } +#endif /* CONFIG_WPS_NFC */ + p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; @@ -713,20 +792,17 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; dev->go_state = go ? LOCAL_GO : REMOTE_GO; - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " - "channel preference: %d MHz", dev->oper_freq); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); if (p2p->state != P2P_IDLE) p2p_stop_find_for_freq(p2p, rx_freq); p2p_set_state(p2p, P2P_GO_NEG); @@ -734,6 +810,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, dev->dialog_token = msg.dialog_token; os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); p2p->go_neg_peer = dev; + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); status = P2P_SC_SUCCESS; } @@ -745,17 +822,14 @@ fail: p2p_parse_free(&msg); if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Response"); + p2p_dbg(p2p, "Sending GO Negotiation Response"); if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); return; } @@ -778,9 +852,8 @@ fail: P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 250) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -798,14 +871,16 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, u8 group_capab; size_t extra = 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Building GO Negotiation Confirm"); + p2p_dbg(p2p, "Building GO Negotiation Confirm"); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -850,6 +925,9 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + return buf; } @@ -858,20 +936,17 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; - struct wpabuf *conf; int go = -1; struct p2p_message msg; u8 status = P2P_SC_SUCCESS; int freq; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Response from " MACSTR + p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } @@ -880,45 +955,42 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Was not expecting GO Negotiation Response - " - "ignore"); + p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; if (msg.dialog_token != dev->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status attribute received"); + p2p_dbg(p2p, "No Status attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation rejected: status %d", - *msg.status); + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); dev->go_neg_req_sent = 0; if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Wait for the peer to become ready for " - "GO Negotiation"); + p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation"); dev->flags |= P2P_DEV_NOT_YET_READY; - dev->wait_count = 0; - p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, + NULL); + eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout, + p2p, NULL); + if (p2p->state == P2P_CONNECT_LISTEN) + p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); + else + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); p2p_set_timeout(p2p, 0, 0); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Stop GO Negotiation attempt"); - p2p_go_neg_failed(p2p, dev, *msg.status); + p2p_dbg(p2p, "Stop GO Negotiation attempt"); + p2p_go_neg_failed(p2p, *msg.status); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_parse_free(&msg); @@ -926,9 +998,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (!msg.capability) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Capability attribute missing from GO " - "Negotiation Response"); + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -936,9 +1006,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (!msg.p2p_device_info) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Device Info attribute missing " - "from GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -946,22 +1014,18 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (!msg.intended_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Intended P2P Interface Address attribute " - "received"); + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.go_intent) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No GO Intent attribute received"); + p2p_dbg(p2p, "No GO Intent attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid GO Intent value (%u) received", + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -969,8 +1033,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Incompatible GO Intent"); + p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; goto fail; } @@ -980,20 +1043,14 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (!go) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Group ID attribute missing from " - "GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response"); p2p->ssid_len = 0; -#ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; -#endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Configuration Timeout attribute " - "missing from GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -1008,76 +1065,81 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, * Note: P2P Client may omit Operating Channel attribute to * indicate it does not have a preference. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Operating Channel attribute received"); + p2p_dbg(p2p, "No Operating Channel attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Channel List attribute received"); + p2p_dbg(p2p, "No Channel List attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (msg.operating_channel) { - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " - "channel preference: %d MHz", dev->oper_freq); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); } else dev->oper_freq = 0; switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: PIN from peer Display"); + p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_USER_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer entered PIN on Keypad"); + p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; case DEV_PW_PUSHBUTTON: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer using pushbutton"); + p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_P2PS_DEFAULT: + p2p_dbg(p2p, "P2P: Peer using P2PS default pin"); + if (dev->wps_method != WPS_P2PS) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", p2p_wps_method_str(dev->wps_method)); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported Device Password ID %d", + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + } + p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; @@ -1089,18 +1151,19 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); fail: - conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, - msg.operating_channel, go); + /* Store GO Negotiation Confirmation to allow retransmission */ + wpabuf_free(dev->go_neg_conf); + dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, + status, msg.operating_channel, + go); p2p_parse_free(&msg); - if (conf == NULL) + if (dev->go_neg_conf == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Confirm"); + p2p_dbg(p2p, "Sending GO Negotiation Confirm"); if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; dev->go_state = go ? LOCAL_GO : REMOTE_GO; @@ -1110,13 +1173,22 @@ fail: freq = rx_freq; else freq = dev->listen_freq; + + dev->go_neg_conf_freq = freq; + dev->go_neg_conf_sent = 0; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, - wpabuf_head(conf), wpabuf_len(conf), 0) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); - p2p_go_neg_failed(p2p, dev, -1); + wpabuf_head(dev->go_neg_conf), + wpabuf_len(dev->go_neg_conf), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + p2p_go_neg_failed(p2p, -1); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } else + dev->go_neg_conf_sent++; + if (status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "GO Negotiation failed"); + p2p_go_neg_failed(p2p, status); } - wpabuf_free(conf); } @@ -1126,22 +1198,18 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; struct p2p_message msg; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Confirm from " MACSTR, + p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting " - "for TX status on GO Negotiation Response since we " - "already received Confirmation"); + p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation"); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } @@ -1149,31 +1217,28 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Was not expecting GO Negotiation Confirm - " - "ignore"); + p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore"); + p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (msg.dialog_token != dev->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status attribute received"); + p2p_dbg(p2p, "No Status attribute received"); p2p_parse_free(&msg); return; } if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation rejected: status %d", - *msg.status); + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); + p2p_go_neg_failed(p2p, *msg.status); p2p_parse_free(&msg); return; } @@ -1183,30 +1248,31 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (dev->go_state == REMOTE_GO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Group ID attribute missing from " - "GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation"); p2p->ssid_len = 0; -#ifdef CONFIG_P2P_STRICT + p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; -#endif /* CONFIG_P2P_STRICT */ } if (!msg.operating_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Operating Channel attribute missing " - "from GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ + } else if (dev->go_state == REMOTE_GO) { + int oper_freq = p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + if (oper_freq != dev->oper_freq) { + p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz", + dev->oper_freq, oper_freq); + dev->oper_freq = oper_freq; + } } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Operating Channel attribute missing " - "from GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; @@ -1220,9 +1286,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, * This should not happen since GO negotiation has already * been completed. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected GO Neg state - do not know which end " - "becomes GO"); + p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO"); return; } @@ -1234,8 +1298,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, * the group so that we will remain on the current channel to * acknowledge any possible retransmission from the peer. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: 20 ms wait on current " - "channel before starting group"); + p2p_dbg(p2p, "20 ms wait on current channel before starting group"); os_sleep(0, 20000); p2p_go_complete(p2p, dev); diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 86873205b830..41ca99faaf48 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -11,6 +11,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "wps/wps_i.h" #include "p2p_i.h" @@ -154,6 +155,7 @@ static void p2p_group_add_common_ies(struct p2p_group *group, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (group->num_members >= group->cfg->max_clients) group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } @@ -169,6 +171,39 @@ static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa) } +static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + size_t len; + + if (subelems == NULL) + return NULL; + + len = wpabuf_len(subelems) + 100; + + ie = wpabuf_alloc(len); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + 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, P2P_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) { struct wpabuf *ie; @@ -180,6 +215,10 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) extra = wpabuf_len(group->p2p->wfd_ie_beacon); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]) + extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]); + ie = wpabuf_alloc(257 + extra); if (ie == NULL) return NULL; @@ -189,6 +228,11 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]) + wpabuf_put_buf(ie, + group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]); + len = p2p_buf_add_ie_hdr(ie); p2p_group_add_common_ies(group, ie); p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); @@ -345,8 +389,8 @@ wifi_display_build_go_ie(struct p2p_group *group) } else { WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - 2); - wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors", - count); + p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors", + count); } wfd_ie = wifi_display_encaps(wfd_subelems); @@ -364,46 +408,69 @@ static void wifi_display_group_update(struct p2p_group *group) #endif /* CONFIG_WIFI_DISPLAY */ -static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients) { u8 *group_info; - struct wpabuf *ie; + int count = 0; struct p2p_group_member *m; - u8 *len; - size_t extra = 0; -#ifdef CONFIG_WIFI_DISPLAY - if (group->wfd_ie) - extra += wpabuf_len(group->wfd_ie); -#endif /* CONFIG_WIFI_DISPLAY */ + p2p_dbg(group->p2p, "* P2P Group Info"); + group_info = wpabuf_put(buf, 0); + wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(buf, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) { + p2p_client_info(buf, m); + count++; + if (max_clients >= 0 && count >= max_clients) + break; + } + WPA_PUT_LE16(group_info + 1, + (u8 *) wpabuf_put(buf, 0) - group_info - 3); +} - ie = wpabuf_alloc(257 + extra); - if (ie == NULL) - return NULL; -#ifdef CONFIG_WIFI_DISPLAY - if (group->wfd_ie) - wpabuf_put_buf(ie, group->wfd_ie); -#endif /* CONFIG_WIFI_DISPLAY */ +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf) +{ + p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid, + group->cfg->ssid_len); +} - len = p2p_buf_add_ie_hdr(ie); - p2p_group_add_common_ies(group, ie); - p2p_group_add_noa(ie, group->noa); +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + struct wpabuf *p2p_subelems, *ie; + + p2p_subelems = wpabuf_alloc(500); + if (p2p_subelems == NULL) + return NULL; + + p2p_group_add_common_ies(group, p2p_subelems); + p2p_group_add_noa(p2p_subelems, group->noa); /* P2P Device Info */ - p2p_buf_add_device_info(ie, group->p2p, NULL); - - /* P2P Group Info */ - group_info = wpabuf_put(ie, 0); - wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO); - wpabuf_put_le16(ie, 0); /* Length to be filled */ - for (m = group->members; m; m = m->next) - p2p_client_info(ie, m); - WPA_PUT_LE16(group_info + 1, - (u8 *) wpabuf_put(ie, 0) - group_info - 3); + p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL); - p2p_buf_update_ie_hdr(ie, len); + /* P2P Group Info: Only when at least one P2P Client is connected */ + if (group->members) + p2p_buf_add_group_info(group, p2p_subelems, -1); + + ie = p2p_group_encaps_probe_resp(p2p_subelems); + wpabuf_free(p2p_subelems); + + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) { + struct wpabuf *extra; + extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]); + ie = wpabuf_concat(extra, ie); + } + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) { + struct wpabuf *wfd = wpabuf_dup(group->wfd_ie); + ie = wpabuf_concat(wfd, ie); + } +#endif /* CONFIG_WIFI_DISPLAY */ return ie; } @@ -537,6 +604,8 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, if (group == NULL) return -1; + p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0); + m = os_zalloc(sizeof(*m)); if (m == NULL) return -1; @@ -556,7 +625,7 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, m->next = group->members; group->members = m; group->num_members++; - wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR + p2p_dbg(group->p2p, "Add client " MACSTR " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, m->client_info ? 1 : 0, @@ -582,6 +651,10 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) extra = wpabuf_len(group->wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]) + extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]); + /* * (Re)Association Response - P2P IE * Status attribute (shall be present when association request is @@ -597,6 +670,11 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) wpabuf_put_buf(resp, group->wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]) + wpabuf_put_buf(resp, + group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]); + rlen = p2p_buf_add_ie_hdr(resp); if (status != P2P_SC_SUCCESS) p2p_buf_add_status(resp, status); @@ -609,8 +687,8 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) { if (p2p_group_remove_member(group, addr)) { - wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove " - "client " MACSTR " from group; num_members=%u/%u", + p2p_dbg(group->p2p, "Remove client " MACSTR + " from group; num_members=%u/%u", MAC2STR(addr), group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients - 1) @@ -822,20 +900,18 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, m = p2p_group_get_client(group, dev_id); if (m == NULL || m->client_info == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this " - "group " MACSTR, - MAC2STR(group->cfg->interface_addr)); + p2p_dbg(group->p2p, "Requested client was not in this group " + MACSTR, MAC2STR(group->cfg->interface_addr)); return -1; } if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_printf(MSG_DEBUG, "P2P: Requested client does not support " - "client discoverability"); + p2p_dbg(group->p2p, "Requested client does not support client discoverability"); return -1; } - wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be " - "sent to " MACSTR, MAC2STR(dev_id)); + p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to " + MACSTR, MAC2STR(dev_id)); req = p2p_build_go_disc_req(); if (req == NULL) @@ -850,8 +926,7 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, group->cfg->interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); @@ -876,7 +951,7 @@ u8 p2p_group_presence_req(struct p2p_group *group, m = p2p_group_get_client_iface(group, client_interface_addr); if (m == NULL || m->client_info == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Client was not in this group"); + p2p_dbg(group->p2p, "Client was not in this group"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } @@ -889,9 +964,9 @@ u8 p2p_group_presence_req(struct p2p_group *group, else curr_noa_len = -1; if (curr_noa_len < 0) - wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA"); + p2p_dbg(group->p2p, "Failed to fetch current NoA"); else if (curr_noa_len == 0) - wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized"); + p2p_dbg(group->p2p, "No NoA being advertized"); else wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, curr_noa_len); @@ -906,10 +981,22 @@ u8 p2p_group_presence_req(struct p2p_group *group, unsigned int p2p_get_group_num_members(struct p2p_group *group) { + if (!group) + return 0; + return group->num_members; } +int p2p_client_limit_reached(struct p2p_group *group) +{ + if (!group || !group->cfg) + return 1; + + return group->num_members >= group->cfg->max_clients; +} + + const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) { struct p2p_group_member *iter = *next; @@ -924,7 +1011,7 @@ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) if (!iter) return NULL; - return iter->addr; + return iter->dev_addr; } @@ -951,3 +1038,36 @@ int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid, group->cfg->ssid_len) == 0; } + + +void p2p_group_force_beacon_update_ies(struct p2p_group *group) +{ + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_get_freq(struct p2p_group *group) +{ + return group->cfg->freq; +} + + +const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group) +{ + return group->cfg; +} + + +void p2p_loop_on_all_groups(struct p2p_data *p2p, + int (*group_callback)(struct p2p_group *group, + void *user_data), + void *user_data) +{ + unsigned int i; + + for (i = 0; i < p2p->num_groups; i++) { + if (!group_callback(p2p->groups[i], user_data)) + break; + } +} diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 27fef0182941..6af19ceda450 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -12,6 +12,17 @@ #include "utils/list.h" #include "p2p.h" +#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 + +enum p2p_role_indication; + +/* + * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN + * must equal 248 or less. Must have a minimum size of 19. + */ +#define MAX_SVC_ADV_LEN 600 +#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240))) + enum p2p_go_state { UNKNOWN_GO, LOCAL_GO, @@ -23,9 +34,11 @@ enum p2p_go_state { */ struct p2p_device { struct dl_list list; - struct os_time last_seen; + struct os_reltime last_seen; int listen_freq; + int oob_go_neg_freq; enum p2p_wps_method wps_method; + u16 oob_pw_id; struct p2p_peer_info info; @@ -52,6 +65,7 @@ struct p2p_device { int go_neg_req_sent; enum p2p_go_state go_state; u8 dialog_token; + u8 tie_breaker; u8 intended_addr[ETH_ALEN]; char country[3]; @@ -76,8 +90,6 @@ struct p2p_device { #define P2P_DEV_PROBE_REQ_ONLY BIT(0) #define P2P_DEV_REPORTED BIT(1) #define P2P_DEV_NOT_YET_READY BIT(2) -#define P2P_DEV_SD_INFO BIT(3) -#define P2P_DEV_SD_SCHEDULE BIT(4) #define P2P_DEV_PD_PEER_DISPLAY BIT(5) #define P2P_DEV_PD_PEER_KEYPAD BIT(6) #define P2P_DEV_USER_REJECTED BIT(7) @@ -91,18 +103,40 @@ struct p2p_device { #define P2P_DEV_REPORTED_ONCE BIT(15) #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) +#define P2P_DEV_NO_PREF_CHAN BIT(18) +#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19) +#define P2P_DEV_P2PS_REPORTED BIT(20) +#define P2P_DEV_PD_PEER_P2PS BIT(21) unsigned int flags; int status; /* enum p2p_status_code */ unsigned int wait_count; unsigned int connect_reqs; unsigned int invitation_reqs; + unsigned int sd_reqs; u16 ext_listen_period; u16 ext_listen_interval; u8 go_timeout; u8 client_timeout; + + /** + * go_neg_conf_sent - Number of GO Negotiation Confirmation retries + */ + u8 go_neg_conf_sent; + + /** + * freq - Frquency on which the GO Negotiation Confirmation is sent + */ + int go_neg_conf_freq; + + /** + * go_neg_conf - GO Negotiation Confirmation frame + */ + struct wpabuf *go_neg_conf; + + int sd_pending_bcast_queries; }; struct p2p_sd_query { @@ -203,16 +237,6 @@ struct p2p_data { * P2P_INVITE_LISTEN - Listen during Invite */ P2P_INVITE_LISTEN, - - /** - * P2P_SEARCH_WHEN_READY - Waiting to start Search - */ - P2P_SEARCH_WHEN_READY, - - /** - * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search - */ - P2P_CONTINUE_SEARCH_WHEN_READY, } state; /** @@ -245,8 +269,17 @@ struct p2p_data { */ struct p2p_device *invite_peer; + /** + * last_p2p_find_oper - Pointer to last pre-find operation peer + */ + struct p2p_device *last_p2p_find_oper; + const u8 *invite_go_dev_addr; u8 invite_go_dev_addr_buf[ETH_ALEN]; + int invite_dev_pw_id; + + unsigned int retry_invite_req:1; + unsigned int retry_invite_req_sent:1; /** * sd_peer - Pointer to Service Discovery peer @@ -258,6 +291,12 @@ struct p2p_data { */ struct p2p_sd_query *sd_query; + /** + * num_p2p_sd_queries - Total number of broadcast SD queries present in + * the list + */ + int num_p2p_sd_queries; + /* GO Negotiation data */ /** @@ -314,6 +353,8 @@ struct p2p_data { */ struct p2p_channels channels; + struct wpa_freq_range_list no_go_freq; + enum p2p_pending_action_state { P2P_NO_PENDING_ACTION, P2P_PENDING_GO_NEG_REQUEST, @@ -322,6 +363,7 @@ struct p2p_data { P2P_PENDING_GO_NEG_CONFIRM, P2P_PENDING_SD, P2P_PENDING_PD, + P2P_PENDING_PD_RESPONSE, P2P_PENDING_INVITATION_REQUEST, P2P_PENDING_INVITATION_RESPONSE, P2P_PENDING_DEV_DISC_REQUEST, @@ -383,6 +425,8 @@ struct p2p_data { } start_after_scan; u8 after_scan_peer[ETH_ALEN]; struct p2p_pending_action_tx *after_scan_tx; + unsigned int after_scan_tx_in_progress:1; + unsigned int send_action_in_progress:1; /* Requested device types for find/search */ unsigned int num_req_dev_types; @@ -390,6 +434,8 @@ struct p2p_data { u8 *find_dev_id; u8 find_dev_id_buf[ETH_ALEN]; + struct os_reltime find_start; /* time of last p2p_find start */ + struct p2p_group **groups; size_t num_groups; @@ -413,6 +459,7 @@ struct p2p_data { int best_freq_24; int best_freq_5; int best_freq_overall; + int own_freq_preference; /** * wps_vendor_ext - WPS Vendor Extensions to add @@ -436,6 +483,14 @@ struct p2p_data { */ int pd_retries; + /** + * pd_force_freq - Forced frequency for PD retries or 0 to auto-select + * + * This is is used during PD retries for join-a-group case to use the + * correct operating frequency determined from a BSS entry for the GO. + */ + int pd_force_freq; + u8 go_timeout; u8 client_timeout; @@ -443,6 +498,20 @@ struct p2p_data { unsigned int search_delay; int in_search_delay; + u8 pending_reg_class; + u8 pending_channel; + u8 pending_channel_forced; + + /* ASP Support */ + struct p2ps_advertisement *p2ps_adv_list; + struct p2ps_provision *p2ps_prov; + u8 wild_card_hash[P2PS_HASH_LEN]; + u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN]; + u8 query_count; + u8 p2ps_seek; + u8 p2ps_seek_count; + u8 p2ps_svc_found; + #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; struct wpabuf *wfd_ie_probe_req; @@ -456,6 +525,10 @@ struct p2p_data { struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; #endif /* CONFIG_WIFI_DISPLAY */ + + u16 authorized_oob_dev_pw_id; + + struct wpabuf **vendor_elem; }; /** @@ -497,6 +570,8 @@ struct p2p_message { const u8 *minor_reason_code; + const u8 *oob_go_neg_channel; + /* P2P Device Info */ const u8 *p2p_device_info; size_t p2p_device_info_len; @@ -508,6 +583,7 @@ struct p2p_message { /* WPS IE */ u16 dev_password_id; + int dev_password_id_present; u16 wps_config_methods; const u8 *wps_pri_dev_type; const u8 *wps_sec_dev_type_list; @@ -522,12 +598,39 @@ struct p2p_message { size_t model_number_len; const u8 *serial_number; size_t serial_number_len; + const u8 *oob_dev_password; + size_t oob_dev_password_len; /* DS Parameter Set IE */ const u8 *ds_params; /* SSID IE */ const u8 *ssid; + + /* P2PS */ + u8 service_hash_count; + const u8 *service_hash; + + const u8 *session_info; + size_t session_info_len; + + const u8 *conn_cap; + + const u8 *adv_id; + const u8 *adv_mac; + + const u8 *adv_service_instance; + size_t adv_service_instance_len; + + const u8 *session_id; + const u8 *session_mac; + + const u8 *feature_cap; + size_t feature_cap_len; + + const u8 *persistent_dev; + const u8 *persistent_ssid; + size_t persistent_ssid_len; }; @@ -551,19 +654,33 @@ struct p2p_group_info { /* p2p_utils.c */ int p2p_random(char *buf, size_t len); -int p2p_channel_to_freq(const char *country, int reg_class, int channel); -int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, - u8 *channel); +int p2p_channel_to_freq(int op_class, int channel); +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); +void p2p_channels_union_inplace(struct p2p_channels *res, + const struct p2p_channels *b); +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res); +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list); int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel); +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan); +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel); +int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, + u8 *op_channel); /* p2p_parse.c */ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg); void p2p_parse_free(struct p2p_message *msg); int p2p_attr_text(struct wpabuf *data, char *buf, char *end); int p2p_group_info_parse(const u8 *gi, size_t gi_len, @@ -586,7 +703,12 @@ u8 p2p_group_presence_req(struct p2p_group *group, int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, size_t group_id_len); void p2p_group_update_ies(struct p2p_group *group); +void p2p_group_force_beacon_update_ies(struct p2p_group *group); struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients); +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf); +int p2p_group_get_freq(struct p2p_group *group); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); @@ -618,8 +740,23 @@ void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval); void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, - int all_attr); +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role); +void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_buf_add_session_info(struct wpabuf *buf, const char *info); +void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap); +void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac); +void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p, + u8 count, const u8 *hash, + struct p2ps_advertisement *adv_list); +void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac); +void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, + const u8 *mask); +void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len); +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -665,7 +802,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, - const u8 *go_dev_addr); + const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); @@ -693,13 +830,12 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg); int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, - unsigned int age_ms, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res); struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr); -void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, - int status); +void p2p_go_neg_failed(struct p2p_data *p2p, int status); void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], @@ -710,5 +846,17 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, + int go); +void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx); +int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status); +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); #endif /* P2P_I_H */ diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 983dd6b36e40..558c6dd0c58f 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -10,13 +10,15 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "p2p_i.h" #include "p2p.h" static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, - const u8 *go_dev_addr) + const u8 *go_dev_addr, + int dev_pw_id) { struct wpabuf *buf; u8 *len; @@ -44,6 +46,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -62,8 +67,11 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, - p2p->op_reg_class, p2p->op_channel); + if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || + !(peer->flags & P2P_DEV_NO_PREF_CHAN)) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); @@ -82,6 +90,14 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); + + if (dev_pw_id >= 0) { + /* WSC IE in Invitation Request for NFC static handover */ + p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); + } + return buf; } @@ -158,13 +174,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, u8 group_bssid[ETH_ALEN], *bssid; int op_freq = 0; u8 reg_class = 0, channel = 0; - struct p2p_channels intersection, *channels = NULL; + struct p2p_channels all_channels, intersection, *channels = NULL; int persistent; os_memset(group_bssid, 0, sizeof(group_bssid)); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Invitation Request from " MACSTR " (freq=%d)", + p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) @@ -172,14 +187,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request from unknown peer " - MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR, + MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1, + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request add device failed " + p2p_dbg(p2p, "Invitation Request add device failed " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; @@ -187,18 +200,16 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, dev = p2p_get_device(p2p, sa); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Reject Invitation Request from unknown " - "peer " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Reject Invitation Request from unknown peer " + MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } } if (!msg.group_id || !msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory attribute missing in Invitation " - "Request from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from " + MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } @@ -211,46 +222,61 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, * the request was for a persistent group if the attribute is * missing. */ - wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags " - "attribute missing from Invitation Request"); + p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request"); persistent = 1; } - if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, + p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels, + &all_channels); + + if (p2p_peer_channels_check(p2p, &all_channels, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels); + p2p_channels_dump(p2p, "own client channels", &all_channels); + p2p_channels_dump(p2p, "peer channels", &dev->channels); + p2p_channels_intersect(&all_channels, &dev->channels, + &intersection); + p2p_channels_dump(p2p, "intersection", &intersection); + if (p2p->cfg->invitation_process) { status = p2p->cfg->invitation_process( p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, - &go, group_bssid, &op_freq, persistent); + &go, group_bssid, &op_freq, persistent, &intersection, + msg.dev_password_id_present ? msg.dev_password_id : -1); + } + + if (go) { + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + p2p_channels_dump(p2p, "intersection(GO)", &intersection); + if (intersection.reg_classes == 0) { + p2p_dbg(p2p, "No common channels found (GO)"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } } if (op_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation " - "processing forced frequency %d MHz", op_freq); - if (p2p_freq_to_channel(p2p->cfg->country, op_freq, - ®_class, &channel) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown forced freq %d MHz from " - "invitation_process()", op_freq); + p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", + op_freq); + if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { + p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()", + op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); if (!p2p_channels_includes(&intersection, reg_class, channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: forced freq %d MHz not in the supported " - "channels interaction", op_freq); + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } @@ -258,28 +284,21 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, if (status == P2P_SC_SUCCESS) channels = &intersection; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No forced channel from invitation processing - " - "figure out best one to use"); + p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use"); - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); /* Default to own configuration as a starting point */ p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own default " - "op_class %d channel %d", + p2p_dbg(p2p, "Own default op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); /* Use peer preference if specified and compatible */ if (msg.operating_channel) { int req_freq; req_freq = p2p_channel_to_freq( - (const char *) msg.operating_channel, msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " - "operating channel preference: %d MHz", + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", req_freq); if (req_freq > 0 && p2p_channels_includes(&intersection, @@ -287,56 +306,47 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, msg.operating_channel[4])) { p2p->op_reg_class = msg.operating_channel[3]; p2p->op_channel = msg.operating_channel[4]; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use peer preference op_class %d " - "channel %d", + p2p_dbg(p2p, "Use peer preference op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot use peer channel " - "preference"); + p2p_dbg(p2p, "Cannot use peer channel preference"); } } - if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + /* Reselect the channel only for the case of the GO */ + if (go && + !p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Initially selected channel (op_class %d " - "channel %d) not in channel intersection - try " - "to reselect", + p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Re-selection result: op_class %d " - "channel %d", + p2p_dbg(p2p, "Re-selection result: op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer does not support selected " - "operating channel (reg_class=%u " - "channel=%u)", + p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)", p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + } else if (go && !(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); } - op_freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown operational channel " - "(country=%c%c reg_class=%u channel=%u)", + p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)", p2p->cfg->country[0], p2p->cfg->country[1], p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " - "channel - %d MHz", op_freq); + p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq); if (status == P2P_SC_SUCCESS) { reg_class = p2p->op_reg_class; @@ -359,12 +369,10 @@ fail: if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); goto out; } @@ -378,12 +386,17 @@ fail: p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; } else p2p->inv_group_bssid_ptr = NULL; - if (msg.group_id_len - ETH_ALEN <= 32) { - os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, - msg.group_id_len - ETH_ALEN); - p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + if (msg.group_id) { + if (msg.group_id_len - ETH_ALEN <= 32) { + os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, + msg.group_id_len - ETH_ALEN); + p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + } + os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); + } else { + p2p->inv_ssid_len = 0; + os_memset(p2p->inv_go_dev_addr, 0, ETH_ALEN); } - os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); p2p->inv_status = status; p2p->inv_op_freq = op_freq; @@ -391,8 +404,7 @@ fail: if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } out: @@ -406,40 +418,120 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, { struct p2p_device *dev; struct p2p_message msg; + struct p2p_channels intersection, *channels = NULL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Invitation Response from " MACSTR, + p2p_dbg(p2p, "Received Invitation Response from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Invitation Response from unknown peer " + p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " MACSTR, MAC2STR(sa)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } if (dev != p2p->invite_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected Invitation Response from peer " + p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " MACSTR, MAC2STR(sa)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } - if (p2p_parse(data, len, &msg)) + if (p2p_parse(data, len, &msg)) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; + } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Status attribute missing in " - "Invitation Response from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " + MACSTR, MAC2STR(sa)); p2p_parse_free(&msg); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } - if (p2p->cfg->invitation_result) + /* + * We should not really receive a replayed response twice since + * duplicate frames are supposed to be dropped. However, not all drivers + * do that for pre-association frames. We did not use to verify dialog + * token matches for invitation response frames, but that check can be + * safely used to drop a replayed response to the previous Invitation + * Request in case the suggested operating channel was changed. This + * allows a duplicated reject frame to be dropped with the assumption + * that the real response follows after it. + */ + if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && + p2p->retry_invite_req_sent && + msg.dialog_token != dev->dialog_token) { + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && + p2p->retry_invite_req && + p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, + &p2p->op_channel) == 0) { + p2p->retry_invite_req = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_INVITE); + p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel", + p2p->op_reg_class, p2p->op_channel); + p2p->retry_invite_req_sent = 1; + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, + p2p->invite_dev_pw_id); + p2p_parse_free(&msg); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->retry_invite_req = 0; + + if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { + p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " + MACSTR, MAC2STR(sa)); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + /* Try to survive without peer channel list */ + channels = &p2p->channels; + } else if (!msg.channel_list) { + /* Non-success cases are not required to include Channel List */ + channels = &p2p->channels; + } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, "No common channels found"); + p2p_parse_free(&msg); + return; + } else { + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + channels = &intersection; + } + + if (p2p->cfg->invitation_result) { + int peer_oper_freq = 0; + int freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + + if (msg.operating_channel) { + peer_oper_freq = p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + if (peer_oper_freq < 0) + peer_oper_freq = 0; + } + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, - msg.group_bssid); + msg.group_bssid, channels, sa, + freq, peer_oper_freq); + } p2p_parse_free(&msg); @@ -450,38 +542,39 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, - const u8 *go_dev_addr) + const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *req; int freq; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send Invitation Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Invitation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } - req = p2p_build_invitation_req(p2p, dev, go_dev_addr); + req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id); if (req == NULL) return -1; if (p2p->state != P2P_IDLE) p2p_stop_listen_for_freq(p2p, freq); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Invitation Request"); + p2p_dbg(p2p, "Sending Invitation Request"); p2p_set_state(p2p, P2P_INVITE); p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; p2p->invite_peer = dev; dev->invitation_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); + } else { + dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK; } wpabuf_free(req); @@ -492,31 +585,34 @@ int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, void p2p_invitation_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request TX callback: success=%d", success); + p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success); if (p2p->invite_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Invite"); + p2p_dbg(p2p, "No pending Invite"); return; } + if (success) + p2p->invite_peer->flags &= ~P2P_DEV_WAIT_INV_REQ_ACK; + /* * Use P2P find, if needed, to find the other device from its listen * channel. */ p2p_set_state(p2p, P2P_INVITE); - p2p_set_timeout(p2p, 0, 100000); + p2p_set_timeout(p2p, 0, success ? 500000 : 100000); } void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Response TX callback: success=%d", success); + p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - if (success && p2p->cfg->invitation_received) { + if (!success) + p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported"); + + if (p2p->cfg->invitation_received) { p2p->cfg->invitation_received(p2p->cfg->cb_ctx, p2p->inv_sa, p2p->inv_group_bssid_ptr, @@ -531,41 +627,55 @@ void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) 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) + int persistent_group, unsigned int pref_freq, int dev_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to invite peer " MACSTR " role=%d persistent=%d " + p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d " "force_freq=%u", MAC2STR(peer), role, persistent_group, force_freq); if (bssid) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid)); + p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid)); if (go_dev_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation for GO Device Address " MACSTR, + p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR, MAC2STR(go_dev_addr)); os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; } else p2p->invite_go_dev_addr = NULL; - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID", + wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID", ssid, ssid_len); + if (dev_pw_id >= 0) { + p2p_dbg(p2p, "Invitation to use Device Password ID %d", + dev_pw_id); + } + p2p->invite_dev_pw_id = dev_pw_id; + p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO && + persistent_group && !force_freq; + p2p->retry_invite_req_sent = 0; dev = p2p_get_device(p2p, peer); - if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot invite unknown P2P Device " MACSTR, + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && + dev->oob_go_neg_freq <= 0)) { + p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR, MAC2STR(peer)); return -1; } + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + role != P2P_INVITE_ROLE_CLIENT) < 0) + return -1; + + if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && + !pref_freq) + dev->flags |= P2P_DEV_NO_PREF_CHAN; + else + dev->flags &= ~P2P_DEV_NO_PREF_CHAN; + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot invite a P2P Device " MACSTR + p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer)); } @@ -574,26 +684,6 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, dev->invitation_reqs = 0; - if (force_freq) { - if (p2p_freq_to_channel(p2p->cfg->country, force_freq, - &p2p->op_reg_class, &p2p->op_channel) < - 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported frequency %u MHz", - force_freq); - return -1; - } - p2p->channels.reg_classes = 1; - p2p->channels.reg_class[0].channels = 1; - p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; - p2p->channels.reg_class[0].channel[0] = p2p->op_channel; - } else { - p2p->op_reg_class = p2p->cfg->op_reg_class; - p2p->op_channel = p2p->cfg->op_channel; - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); - } - if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); @@ -604,5 +694,5 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, os_memcpy(p2p->inv_ssid, ssid, ssid_len); p2p->inv_ssid_len = ssid_len; p2p->inv_persistent = persistent_group; - return p2p_invite_send(p2p, dev, go_dev_addr); + return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); } diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index 097a31de19d3..fd6a4610d839 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -268,6 +268,125 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", *msg->minor_reason_code); break; + case P2P_ATTR_OOB_GO_NEG_CHANNEL: + if (len < 6) { + wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg " + "Channel attribute (length %d)", len); + return -1; + } + msg->oob_go_neg_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: " + "Country %c%c(0x%02x) Operating Class %d " + "Channel Number %d Role %d", + data[0], data[1], data[2], data[3], data[4], + data[5]); + break; + case P2P_ATTR_SERVICE_HASH: + if (len < P2PS_HASH_LEN) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Service Hash (length %u)", + len); + return -1; + } + msg->service_hash_count = len / P2PS_HASH_LEN; + msg->service_hash = data; + wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len); + break; + case P2P_ATTR_SESSION_INFORMATION_DATA: + msg->session_info = data; + msg->session_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p", + len, data); + break; + case P2P_ATTR_CONNECTION_CAPABILITY: + if (len < 1) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Connection Capability (length %u)", + len); + return -1; + } + msg->conn_cap = data; + wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x", + *msg->conn_cap); + break; + case P2P_ATTR_ADVERTISEMENT_ID: + if (len < 10) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Advertisement ID (length %u)", + len); + return -1; + } + msg->adv_id = data; + msg->adv_mac = &data[sizeof(u32)]; + wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x", + WPA_GET_LE32(data)); + break; + case P2P_ATTR_ADVERTISED_SERVICE: + if (len < 8) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Service Instance (length %u)", + len); + return -1; + } + msg->adv_service_instance = data; + msg->adv_service_instance_len = len; + if (len <= 255 + 8) { + char str[256]; + u8 namelen; + + namelen = data[6]; + if (namelen > len - 7) + break; + os_memcpy(str, &data[7], namelen); + str[namelen] = '\0'; + wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s", + WPA_GET_LE32(data), str); + } else { + wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p", + data); + } + break; + case P2P_ATTR_SESSION_ID: + if (len < sizeof(u32) + ETH_ALEN) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Session ID Info (length %u)", + len); + return -1; + } + msg->session_id = data; + msg->session_mac = &data[sizeof(u32)]; + wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR, + WPA_GET_LE32(data), MAC2STR(msg->session_mac)); + break; + case P2P_ATTR_FEATURE_CAPABILITY: + if (!len) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Feature Capability (length %u)", + len); + return -1; + } + msg->feature_cap = data; + msg->feature_cap_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len); + break; + case P2P_ATTR_PERSISTENT_GROUP: + { + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, + "P2P: Too short Persistent Group Info (length %u)", + len); + return -1; + } + + msg->persistent_dev = data; + msg->persistent_ssid_len = len - ETH_ALEN; + msg->persistent_ssid = &data[ETH_ALEN]; + wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s", + MAC2STR(msg->persistent_dev), + wpa_ssid_txt(msg->persistent_ssid, + msg->persistent_ssid_len)); + break; + } default: wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " "(length %d)", id, len); @@ -296,23 +415,27 @@ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg) while (pos < end) { u16 attr_len; - if (pos + 2 >= end) { + u8 id; + + if (end - pos < 3) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute"); return -1; } - attr_len = WPA_GET_LE16(pos + 1); + id = *pos++; + attr_len = WPA_GET_LE16(pos); + pos += 2; wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u", - pos[0], attr_len); - if (pos + 3 + attr_len > end) { + id, attr_len); + if (attr_len > end - pos) { wpa_printf(MSG_DEBUG, "P2P: Attribute underflow " "(len=%u left=%d)", - attr_len, (int) (end - pos - 3)); + attr_len, (int) (end - pos)); wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos); return -1; } - if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg)) + if (p2p_parse_attribute(id, pos, attr_len, msg)) return -1; - pos += 3 + attr_len; + pos += attr_len; } return 0; @@ -340,6 +463,7 @@ static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", msg->dev_password_id); + msg->dev_password_id_present = 1; } if (attr.primary_dev_type) { char devtype[WPS_DEV_TYPE_BUFSIZE]; @@ -367,6 +491,9 @@ static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) msg->serial_number = attr.serial_number; msg->serial_number_len = attr.serial_number_len; + msg->oob_dev_password = attr.oob_dev_password; + msg->oob_dev_password_len = attr.oob_dev_password_len; + return 0; } @@ -450,6 +577,33 @@ int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg) } +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + + msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + + return 0; +} + + /** * p2p_parse_free - Free temporary data from P2P parsing * @msg: Parsed attributes @@ -559,7 +713,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, "dev=" MACSTR " iface=" MACSTR, MAC2STR(cli->p2p_device_addr), MAC2STR(cli->p2p_interface_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -570,7 +724,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, wps_dev_type_bin2str(cli->pri_dev_type, devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -579,7 +733,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, wps_dev_type_bin2str( &cli->sec_dev_types[s * 8], devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -594,7 +748,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, } ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -628,7 +782,7 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) "p2p_dev_capab=0x%x\n" "p2p_group_capab=0x%x\n", msg.capability[0], msg.capability[1]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -640,14 +794,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) wps_dev_type_bin2str(msg.pri_dev_type, devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n", msg.device_name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -655,14 +809,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR "\n", MAC2STR(msg.p2p_device_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n", msg.config_methods); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index ca33f17a17b8..328b1e029ce5 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "p2p_i.h" #include "p2p.h" @@ -39,20 +40,145 @@ static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, } +static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) +{ + int found; + u8 intended_addr[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + int group_iface; + + if (!p2p->cfg->get_go_info) + return; + + found = p2p->cfg->get_go_info( + p2p->cfg->cb_ctx, intended_addr, ssid, + &ssid_len, &group_iface); + if (found) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, + ssid, ssid_len); + p2p_buf_add_intended_addr(buf, intended_addr); + } else { + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + + /* Add pre-composed P2P Group ID */ + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, + p2p->ssid, p2p->ssid_len); + + if (group_iface) + p2p_buf_add_intended_addr( + buf, p2p->intended_addr); + else + p2p_buf_add_intended_addr( + buf, p2p->cfg->dev_addr); + } +} + + +static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, + struct wpabuf *buf, u16 config_methods) +{ + struct p2ps_provision *prov = p2p->p2ps_prov; + u8 feat_cap_mask[] = { 1, 0 }; + int shared_group = 0; + u8 ssid[32]; + size_t ssid_len; + u8 go_dev_addr[ETH_ALEN]; + + /* If we might be explicite group owner, add GO details */ + if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | + P2PS_SETUP_NEW)) + p2ps_add_new_group_info(p2p, buf); + + if (prov->status >= 0) + p2p_buf_add_status(buf, (u8) prov->status); + else + prov->method = config_methods; + + if (p2p->cfg->get_persistent_group) { + shared_group = p2p->cfg->get_persistent_group( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, + go_dev_addr, ssid, &ssid_len); + } + + /* Add Operating Channel if conncap includes GO */ + if (shared_group || + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | + P2PS_SETUP_NEW))) { + u8 tmp; + + p2p_go_select_channel(p2p, dev, &tmp); + + if (p2p->op_reg_class && p2p->op_channel) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + else + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel); + } + + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels); + + if (prov->info[0]) + p2p_buf_add_session_info(buf, prov->info); + + p2p_buf_add_connection_capability(buf, prov->conncap); + + p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac); + + if (shared_group || prov->conncap == P2PS_SETUP_NEW || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { + /* Add Config Timeout */ + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + } + + p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + + p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); + + p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), + feat_cap_mask); + + if (shared_group) + p2p_buf_add_persistent_group_info(buf, go_dev_addr, + ssid, ssid_len); +} + + static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, - u8 dialog_token, - u16 config_methods, - struct p2p_device *go) + struct p2p_device *dev, + int join) { struct wpabuf *buf; u8 *len; size_t extra = 0; + u8 dialog_token = dev->dialog_token; + u16 config_methods = dev->req_config_methods; + struct p2p_device *go = join ? dev : NULL; + u8 group_capab; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_prov_disc_req) extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); + + if (p2p->p2ps_prov) + extra += os_strlen(p2p->p2ps_prov->info) + 1 + + sizeof(struct p2ps_provision); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -60,10 +186,23 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); len = p2p_buf_add_ie_hdr(buf); + + group_capab = 0; + if (p2p->p2ps_prov) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } p2p_buf_add_capability(buf, p2p->dev_capab & - ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); p2p_buf_add_device_info(buf, p2p, NULL); - if (go) { + if (p2p->p2ps_prov) { + p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods); + } else if (go) { p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, go->oper_ssid_len); } @@ -77,18 +216,27 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); + return buf; } static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, + struct p2p_device *dev, u8 dialog_token, + enum p2p_status_code status, u16 config_methods, + u32 adv_id, const u8 *group_id, - size_t group_id_len) + size_t group_id_len, + const u8 *persist_ssid, + size_t persist_ssid_len) { struct wpabuf *buf; size_t extra = 0; + int persist = 0; #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; @@ -111,12 +259,106 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(100 + extra); + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); + + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); + /* Add P2P IE for P2PS */ + if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { + u8 feat_cap_mask[] = { 1, 0 }; + u8 *len = p2p_buf_add_ie_hdr(buf); + struct p2ps_provision *prov = p2p->p2ps_prov; + u8 group_capab; + + if (!status && prov->status != -1) + status = prov->status; + + p2p_buf_add_status(buf, status); + group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP | + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (persist_ssid && p2p->cfg->get_persistent_group && + (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED)) { + u8 ssid[32]; + size_t ssid_len; + u8 go_dev_addr[ETH_ALEN]; + + persist = p2p->cfg->get_persistent_group( + p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + persist_ssid, persist_ssid_len, go_dev_addr, + ssid, &ssid_len); + if (persist) + p2p_buf_add_persistent_group_info( + buf, go_dev_addr, ssid, ssid_len); + } + + if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) + p2ps_add_new_group_info(p2p, buf); + + /* Add Operating Channel if conncap indicates GO */ + if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) { + u8 tmp; + + if (dev) + p2p_go_select_channel(p2p, dev, &tmp); + + if (p2p->op_reg_class && p2p->op_channel) + p2p_buf_add_operating_channel( + buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + else + p2p_buf_add_operating_channel( + buf, p2p->cfg->country, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel); + } + + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->cfg->channels); + + if (!persist && (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED)) + p2p_buf_add_connection_capability(buf, prov->conncap); + + p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); + + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + + p2p_buf_add_session_id(buf, prov->session_id, + prov->session_mac); + + p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask), + feat_cap_mask); + p2p_buf_update_ie_hdr(buf, len); + } else if (status != P2P_SC_SUCCESS || adv_id) { + u8 *len = p2p_buf_add_ie_hdr(buf); + + p2p_buf_add_status(buf, status); + + if (p2p->p2ps_prov) + p2p_buf_add_advertisement_id(buf, adv_id, + p2p->p2ps_prov->adv_mac); + + p2p_buf_update_ie_hdr(buf, len); + } + /* WPS IE with Config Methods attribute */ p2p_build_wps_ie_config_methods(buf, config_methods); @@ -125,38 +367,74 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); + return buf; } +static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, + u32 session_id, u16 method, + const u8 *session_mac, const u8 *adv_mac) +{ + struct p2ps_provision *tmp; + + if (!p2p->p2ps_prov) { + p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1); + if (!p2p->p2ps_prov) + return -1; + } else { + os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1); + } + + tmp = p2p->p2ps_prov; + tmp->adv_id = adv_id; + tmp->session_id = session_id; + tmp->method = method; + os_memcpy(tmp->session_mac, session_mac, ETH_ALEN); + os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN); + tmp->info[0] = '\0'; + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_message msg; struct p2p_device *dev; int freq; - int reject = 1; + enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; struct wpabuf *resp; + u32 adv_id = 0; + struct p2ps_advertisement *p2ps_adv = NULL; + u8 conncap = P2PS_SETUP_NEW; + u8 auto_accept = 0; + u32 session_id = 0; + u8 session_mac[ETH_ALEN]; + u8 adv_mac[ETH_ALEN]; + u8 group_mac[ETH_ALEN]; + int passwd_id = DEV_PW_DEFAULT; + u16 config_methods; if (p2p_parse(data, len, &msg)) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Provision Discovery Request from " MACSTR + p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request from " - "unknown peer " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Provision Discovery Request from unknown peer " + MACSTR, MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1, + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request add device " - "failed " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Provision Discovery Request add device failed " + MACSTR, MAC2STR(sa)); } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); @@ -165,13 +443,13 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, if (!(msg.wps_config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | - WPS_CONFIG_PUSHBUTTON))) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported " - "Config Methods in Provision Discovery Request"); + WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) { + p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); goto out; } - if (msg.group_id) { + /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ + if (!msg.adv_id && msg.group_id) { size_t i; for (i = 0; i < p2p->num_groups; i++) { if (p2p_group_is_group_id_match(p2p->groups[i], @@ -180,64 +458,319 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, break; } if (i == p2p->num_groups) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD " - "request for unknown P2P Group ID - reject"); + p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); goto out; } } - if (dev) + if (dev) { dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD); + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); + + /* Remove stale persistent groups */ + if (p2p->cfg->remove_stale_groups) { + p2p->cfg->remove_stale_groups( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + msg.persistent_dev, + msg.persistent_ssid, msg.persistent_ssid_len); + } + } if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + passwd_id = DEV_PW_USER_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); if (dev) dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + passwd_id = DEV_PW_REGISTRAR_SPECIFIED; + } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", + MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_P2PS; + passwd_id = DEV_PW_P2PS_DEFAULT; } - reject = 0; + reject = P2P_SC_SUCCESS; + + os_memset(session_mac, 0, ETH_ALEN); + os_memset(adv_mac, 0, ETH_ALEN); + os_memset(group_mac, 0, ETH_ALEN); + + if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && + (msg.status || msg.conn_cap)) { + u8 remote_conncap; + + if (msg.intended_addr) + os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + + os_memcpy(session_mac, msg.session_mac, ETH_ALEN); + os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + + session_id = WPA_GET_LE32(msg.session_id); + adv_id = WPA_GET_LE32(msg.adv_id); + + if (!msg.status) + p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); + + p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv); + + if (msg.conn_cap) + conncap = *msg.conn_cap; + remote_conncap = conncap; + + if (p2ps_adv) { + auto_accept = p2ps_adv->auto_accept; + conncap = p2p->cfg->p2ps_group_capability( + p2p->cfg->cb_ctx, conncap, auto_accept); + + p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", + auto_accept, remote_conncap, conncap); + + if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { + p2p_dbg(p2p, + "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", + p2ps_adv->config_methods, + msg.wps_config_methods); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!p2ps_adv->state) { + p2p_dbg(p2p, "P2PS state unavailable"); + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else if (!conncap) { + p2p_dbg(p2p, "Conncap resolution failed"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Keypad - always defer"); + auto_accept = 0; + } + + if (auto_accept || reject != P2P_SC_SUCCESS) { + struct p2ps_provision *tmp; + + if (reject == P2P_SC_SUCCESS && !conncap) { + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (p2ps_setup_p2ps_prov( + p2p, adv_id, session_id, + msg.wps_config_methods, + session_mac, adv_mac) < 0) { + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + goto out; + } + + tmp = p2p->p2ps_prov; + if (conncap) { + tmp->conncap = conncap; + tmp->status = P2P_SC_SUCCESS; + } else { + tmp->conncap = auto_accept; + tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (reject != P2P_SC_SUCCESS) + goto out; + } + } else if (!msg.status) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + + if (!msg.status && !auto_accept && + (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { + struct p2ps_provision *tmp; + + if (!conncap) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + + if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, + msg.wps_config_methods, + session_mac, adv_mac) < 0) { + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + goto out; + } + tmp = p2p->p2ps_prov; + reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + tmp->status = reject; + } + + if (msg.status) { + if (*msg.status && + *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED && + p2p->p2ps_prov) { + u16 method = p2p->p2ps_prov->method; + + conncap = p2p->cfg->p2ps_group_capability( + p2p->cfg->cb_ctx, remote_conncap, + p2p->p2ps_prov->conncap); + + p2p_dbg(p2p, + "Conncap: local:%d remote:%d result:%d", + p2p->p2ps_prov->conncap, + remote_conncap, conncap); + + /* + * Ensure that if we asked for PIN originally, + * our method is consistent with original + * request. + */ + if (method & WPS_CONFIG_DISPLAY) + method = WPS_CONFIG_KEYPAD; + else if (method & WPS_CONFIG_KEYPAD) + method = WPS_CONFIG_DISPLAY; + + /* Reject this "Deferred Accept* if incompatible + * conncap or method */ + if (!conncap || + !(msg.wps_config_methods & method)) + reject = + P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + else + reject = P2P_SC_SUCCESS; + + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + } + } + } out: - resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, - reject ? 0 : msg.wps_config_methods, - msg.group_id, msg.group_id_len); + if (reject == P2P_SC_SUCCESS || + reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + config_methods = msg.wps_config_methods; + else + config_methods = 0; + resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject, + config_methods, adv_id, + msg.group_id, msg.group_id_len, + msg.persistent_ssid, + msg.persistent_ssid_len); if (resp == NULL) { p2p_parse_free(&msg); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Provision Discovery Response"); + p2p_dbg(p2p, "Sending Provision Discovery Response"); if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); p2p_parse_free(&msg); return; } - p2p->pending_action_state = P2P_NO_PENDING_ACTION; + p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); - } + p2p_dbg(p2p, "Failed to send Action frame"); + } else + p2p->send_action_in_progress = 1; wpabuf_free(resp); - if (!reject && p2p->cfg->prov_disc_req) { + if (!p2p->cfg->p2ps_prov_complete) { + /* Don't emit anything */ + } else if (msg.status && *msg.status != P2P_SC_SUCCESS && + *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, + sa, adv_mac, session_mac, + NULL, adv_id, session_id, + 0, 0, msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 0, NULL); + } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && + p2p->p2ps_prov) { + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + + if (reject != P2P_SC_SUCCESS) + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, + sa, adv_mac, session_mac, + NULL, adv_id, + session_id, conncap, 0, + msg.persistent_ssid, + msg.persistent_ssid_len, 0, + 0, NULL); + else + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, + *msg.status, + sa, adv_mac, session_mac, + group_mac, adv_id, + session_id, conncap, + passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, 0, + 0, NULL); + } else if (msg.status && p2p->p2ps_prov) { + p2p->p2ps_prov->status = P2P_SC_SUCCESS; + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, + adv_mac, session_mac, group_mac, + adv_id, session_id, conncap, + passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 0, NULL); + } else if (msg.status) { + } else if (auto_accept && reject == P2P_SC_SUCCESS) { + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, + sa, adv_mac, session_mac, + group_mac, adv_id, session_id, + conncap, passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 0, NULL); + } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + (!msg.session_info || !msg.session_info_len)) { + p2p->p2ps_prov->method = msg.wps_config_methods; + + p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, + sa, adv_mac, session_mac, + group_mac, adv_id, session_id, + conncap, passwd_id, + msg.persistent_ssid, + msg.persistent_ssid_len, + 0, 1, NULL); + } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + size_t buf_len = msg.session_info_len; + char *buf = os_malloc(2 * buf_len + 1); + + if (buf) { + p2p->p2ps_prov->method = msg.wps_config_methods; + + utf8_escape((char *) msg.session_info, buf_len, + buf, 2 * buf_len + 1); + + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, + adv_mac, session_mac, group_mac, adv_id, + session_id, conncap, passwd_id, + msg.persistent_ssid, msg.persistent_ssid_len, + 0, 1, buf); + + os_free(buf); + } + } + + if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) { const u8 *dev_addr = sa; if (msg.p2p_device_addr) dev_addr = msg.p2p_device_addr; @@ -259,30 +792,63 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, { struct p2p_message msg; struct p2p_device *dev; - u16 report_config_methods = 0; + u16 report_config_methods = 0, req_config_methods; + u8 status = P2P_SC_SUCCESS; int success = 0; + u32 adv_id = 0; + u8 conncap = P2PS_SETUP_NEW; + u8 adv_mac[ETH_ALEN]; + u8 group_mac[ETH_ALEN]; + int passwd_id = DEV_PW_DEFAULT; if (p2p_parse(data, len, &msg)) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Provision Discovery Response from " MACSTR + /* Parse the P2PS members present */ + if (msg.status) + status = *msg.status; + + if (msg.intended_addr) + os_memcpy(group_mac, msg.intended_addr, ETH_ALEN); + else + os_memset(group_mac, 0, ETH_ALEN); + + if (msg.adv_mac) + os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + else + os_memset(adv_mac, 0, ETH_ALEN); + + if (msg.adv_id) + adv_id = WPA_GET_LE32(msg.adv_id); + + if (msg.conn_cap) { + conncap = *msg.conn_cap; + + /* Switch bits to local relative */ + switch (conncap) { + case P2PS_SETUP_GROUP_OWNER: + conncap = P2PS_SETUP_CLIENT; + break; + case P2PS_SETUP_CLIENT: + conncap = P2PS_SETUP_GROUP_OWNER; + break; + } + } + + p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR " with config methods 0x%x", MAC2STR(sa), msg.wps_config_methods); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Provision Discovery Response from " - MACSTR " with no pending request", MAC2STR(sa)); + p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR + " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; } if (dev->dialog_token != msg.dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Provision Discovery Response with " - "unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; @@ -293,6 +859,12 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->pending_action_state = P2P_NO_PENDING_ACTION; } + /* + * Use a local copy of the requested config methods since + * p2p_reset_pending_pd() can clear this in the peer entry. + */ + req_config_methods = dev->req_config_methods; + /* * If the response is from the peer to whom a user initiated request * was sent earlier, we reset that state info here. @@ -301,28 +873,114 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) p2p_reset_pending_pd(p2p); - if (msg.wps_config_methods != dev->req_config_methods) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " - "our Provision Discovery Request"); + if (msg.wps_config_methods != req_config_methods) { + p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", + msg.wps_config_methods, req_config_methods); if (p2p->cfg->prov_disc_fail) p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, - P2P_PROV_DISC_REJECTED); + P2P_PROV_DISC_REJECTED, + adv_id, adv_mac, NULL); p2p_parse_free(&msg); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; goto out; } - report_config_methods = dev->req_config_methods; + report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD); - if (dev->req_config_methods & WPS_CONFIG_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); + if (req_config_methods & WPS_CONFIG_DISPLAY) { + p2p_dbg(p2p, "Peer " MACSTR " accepted to show a PIN on display", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + passwd_id = DEV_PW_REGISTRAR_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + p2p_dbg(p2p, "Peer " MACSTR " accepted to write our PIN using keypad", MAC2STR(sa)); dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + passwd_id = DEV_PW_USER_SPECIFIED; + } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { + p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_P2PS; + passwd_id = DEV_PW_P2PS_DEFAULT; + } + + if ((msg.conn_cap || msg.persistent_dev) && + msg.adv_id && + (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && + p2p->p2ps_prov) { + if (p2p->cfg->p2ps_prov_complete) { + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, status, sa, adv_mac, + p2p->p2ps_prov->session_mac, + group_mac, adv_id, p2p->p2ps_prov->session_id, + conncap, passwd_id, msg.persistent_ssid, + msg.persistent_ssid_len, 1, 0, NULL); + } + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + } + + if (status != P2P_SC_SUCCESS && + status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && + status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { + if (p2p->cfg->p2ps_prov_complete) + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, status, sa, adv_mac, + p2p->p2ps_prov->session_mac, + group_mac, adv_id, p2p->p2ps_prov->session_id, + 0, 0, NULL, 0, 1, 0, NULL); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + } + + if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + if (p2p->cfg->remove_stale_groups) { + p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + NULL, NULL, 0); + } + + if (msg.session_info && msg.session_info_len) { + size_t info_len = msg.session_info_len; + char *deferred_sess_resp = os_malloc(2 * info_len + 1); + + if (!deferred_sess_resp) { + p2p_parse_free(&msg); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + goto out; + } + utf8_escape((char *) msg.session_info, info_len, + deferred_sess_resp, 2 * info_len + 1); + + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail( + p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_INFO_UNAVAILABLE, + adv_id, adv_mac, + deferred_sess_resp); + os_free(deferred_sess_resp); + } else + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail( + p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_INFO_UNAVAILABLE, + adv_id, adv_mac, NULL); + } else if (msg.wps_config_methods != dev->req_config_methods || + status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_REJECTED, 0, + NULL, NULL); + p2p_parse_free(&msg); + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = NULL; + goto out; } /* Store the provisioning info */ @@ -335,10 +993,8 @@ out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Start GO Neg after the PD-before-GO-Neg " - "workaround with " MACSTR, - MAC2STR(dev->info.p2p_device_addr)); + p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " + MACSTR, MAC2STR(dev->info.p2p_device_addr)); dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; p2p_connect_send(p2p, dev); return; @@ -346,6 +1002,11 @@ out: if (success && p2p->cfg->prov_disc_resp) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); + + if (p2p->state == P2P_PD_DURING_FIND) { + p2p_clear_timeout(p2p); + p2p_continue_find(p2p); + } } @@ -361,9 +1022,8 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send Provision Discovery Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Provision Discovery Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -371,8 +1031,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot use PD with P2P Device " MACSTR + p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(dev->info.p2p_device_addr)); return -1; @@ -380,9 +1039,33 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, /* TODO: use device discoverability request through GO */ } - req = p2p_build_prov_disc_req(p2p, dev->dialog_token, - dev->req_config_methods, - join ? dev : NULL); + if (p2p->p2ps_prov) { + if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) { + if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY) + dev->req_config_methods = WPS_CONFIG_KEYPAD; + else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD) + dev->req_config_methods = WPS_CONFIG_DISPLAY; + else + dev->req_config_methods = WPS_CONFIG_P2PS; + } else { + /* Order of preference, based on peer's capabilities */ + if (p2p->p2ps_prov->method) + dev->req_config_methods = + p2p->p2ps_prov->method; + else if (dev->info.config_methods & WPS_CONFIG_P2PS) + dev->req_config_methods = WPS_CONFIG_P2PS; + else if (dev->info.config_methods & WPS_CONFIG_DISPLAY) + dev->req_config_methods = WPS_CONFIG_DISPLAY; + else + dev->req_config_methods = WPS_CONFIG_KEYPAD; + } + p2p_dbg(p2p, + "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x", + p2p->p2ps_prov->method, p2p->p2ps_prov->status, + dev->req_config_methods); + } + + req = p2p_build_prov_disc_req(p2p, dev, join); if (req == NULL) return -1; @@ -392,8 +1075,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); return -1; } @@ -406,6 +1088,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, 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) { @@ -415,20 +1098,30 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, if (dev == NULL) dev = p2p_get_device_interface(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision " - "Discovery Request destination " MACSTR + p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR " not yet known", MAC2STR(peer_addr)); + os_free(p2ps_prov); return -1; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery " - "Request with " MACSTR " (config methods 0x%x)", + p2p_dbg(p2p, "Provision Discovery Request with " MACSTR + " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); - if (config_methods == 0) + if (config_methods == 0 && !p2ps_prov) { + os_free(p2ps_prov); return -1; + } + + if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED && + p2p->p2ps_prov) { + /* Use cached method from deferred provisioning */ + p2ps_prov->method = p2p->p2ps_prov->method; + } /* Reset provisioning info */ dev->wps_prov_info = 0; + os_free(p2p->p2ps_prov); + p2p->p2ps_prov = p2ps_prov; dev->req_config_methods = config_methods; if (join) @@ -438,14 +1131,14 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && p2p->state != P2P_LISTEN_ONLY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other " - "operations; postpone Provision Discovery Request " - "with " MACSTR " (config methods 0x%x)", + p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " + MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); return 0; } p2p->user_initiated_pd = user_initiated_pd; + p2p->pd_force_freq = force_freq; if (p2p->user_initiated_pd) p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; @@ -481,4 +1174,5 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->user_initiated_pd = 0; os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pd_retries = 0; + p2p->pd_force_freq = 0; } diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index abe1d6b58661..1a2af04b8004 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -52,6 +52,7 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, { struct p2p_sd_query *q; int wsd = 0; + int count = 0; if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) return NULL; /* peer does not support SD */ @@ -64,15 +65,52 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, /* Use WSD only if the peer indicates support or it */ if (q->wsd && !wsd) continue; - if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) - return q; + /* if the query is a broadcast query */ + if (q->for_all_peers) { + /* + * check if there are any broadcast queries pending for + * this device + */ + if (dev->sd_pending_bcast_queries <= 0) + return NULL; + /* query number that needs to be send to the device */ + if (count == dev->sd_pending_bcast_queries - 1) + goto found; + count++; + } if (!q->for_all_peers && os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) == 0) - return q; + goto found; } return NULL; + +found: + if (dev->sd_reqs > 100) { + p2p_dbg(p2p, "Too many SD request attempts to " MACSTR + " - skip remaining queries", + MAC2STR(dev->info.p2p_device_addr)); + return NULL; + } + return q; +} + + +static void p2p_decrease_sd_bc_queries(struct p2p_data *p2p, int query_number) +{ + struct p2p_device *dev; + + p2p->num_p2p_sd_queries--; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (query_number <= dev->sd_pending_bcast_queries - 1) { + /* + * Query not yet sent to the device and it is to be + * removed, so update the pending count. + */ + dev->sd_pending_bcast_queries--; + } + } } @@ -80,10 +118,16 @@ static int p2p_unlink_sd_query(struct p2p_data *p2p, struct p2p_sd_query *query) { struct p2p_sd_query *q, *prev; + int query_number = 0; + q = p2p->sd_queries; prev = NULL; while (q) { if (q == query) { + /* If the query is a broadcast query, decrease one from + * all the devices */ + if (query->for_all_peers) + p2p_decrease_sd_bc_queries(p2p, query_number); if (prev) prev->next = q->next; else @@ -92,6 +136,8 @@ static int p2p_unlink_sd_query(struct p2p_data *p2p, p2p->sd_query = NULL; return 1; } + if (q->for_all_peers) + query_number++; prev = q; q = q->next; } @@ -118,6 +164,7 @@ void p2p_free_sd_queries(struct p2p_data *p2p) q = q->next; p2p_free_sd_query(prev); } + p2p->num_p2p_sd_queries = 0; } @@ -133,8 +180,7 @@ static struct wpabuf * p2p_build_sd_query(u16 update_indic, /* ANQP Query Request Frame */ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ wpabuf_put_buf(buf, tlvs); gas_anqp_set_element_len(buf, len_pos); @@ -157,8 +203,7 @@ static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, wpabuf_head(req), wpabuf_len(req), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); } @@ -181,8 +226,7 @@ static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, if (tlvs) { /* ANQP Query Response Frame */ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); wpabuf_put_buf(buf, tlvs); @@ -213,8 +257,7 @@ static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, /* ANQP Query Response Frame */ wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ wpabuf_put_le16(buf, 3 + 1 + 2 + total_len); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); } @@ -232,12 +275,12 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) int ret = 0; struct p2p_sd_query *query; int freq; + unsigned int wait_time; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send SD Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send SD Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -246,23 +289,25 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) if (query == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Start Service Discovery with " MACSTR, + p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); if (req == NULL) return -1; + dev->sd_reqs++; p2p->sd_peer = dev; p2p->sd_query = query; p2p->pending_action_state = P2P_PENDING_SD; + wait_time = 5000; + if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen) + wait_time = p2p->cfg->max_listen; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 5000) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), wait_time) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); ret = -1; } @@ -290,8 +335,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) return; @@ -300,14 +344,12 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, return; dialog_token = *pos++; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GAS Initial Request from " MACSTR " (dialog token %u, " - "freq %d)", + p2p_dbg(p2p, "GAS Initial Request from " MACSTR + " (dialog token %u, freq %d)", MAC2STR(sa), dialog_token, rx_freq); if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Initial Request: %u", *pos); + p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos); return; } pos++; @@ -315,15 +357,13 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Initial Request"); + p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -342,8 +382,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; @@ -351,30 +390,21 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Request length"); + p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } - if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x", + WPA_GET_BE32(pos)); return; } - pos += 3; - - if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); - return; - } - pos++; + pos += 4; if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, @@ -390,8 +420,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, /* TODO: fix the length limit to match with the maximum frame length */ if (wpabuf_len(resp_tlvs) > 1400) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long " - "enough to require fragmentation"); + p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* * TODO: Could consider storing the fragmented response @@ -400,14 +429,12 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, * Though, that would eat more memory, so there are * also benefits to just using a single buffer. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " - "previous SD response"); + p2p_dbg(p2p, "Drop previous SD response"); wpabuf_free(p2p->sd_resp); } p2p->sd_resp = wpabuf_dup(resp_tlvs); if (p2p->sd_resp == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Failed to " - "allocate SD response fragmentation area"); + p2p_err(p2p, "Failed to allocate SD response fragmentation area"); return; } os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); @@ -417,8 +444,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 1, p2p->srv_update_indic, NULL); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits " - "in initial response"); + p2p_dbg(p2p, "SD response fits in initial response"); resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); @@ -430,8 +456,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } @@ -451,21 +476,18 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected GAS Initial Response from " + p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GAS Initial Response from " MACSTR " (len=%d)", + p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 5 + 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Too short GAS Initial Response frame"); + p2p_dbg(p2p, "Too short GAS Initial Response frame"); return; } @@ -475,20 +497,16 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos += 2; comeback_delay = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: dialog_token=%u status_code=%u comeback_delay=%u", + p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u", dialog_token, status_code, comeback_delay); if (status_code) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery failed: status code %u", + p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Initial Response: %u", - *pos); + p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos); return; } pos++; @@ -496,15 +514,13 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Initial Response"); + p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -512,27 +528,22 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ if (pos + 2 > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " - "Response"); + p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", - slen); + p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " - "Response data"); + p2p_dbg(p2p, "Not enough Query Response data"); return; } end = pos + slen; if (comeback_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented " - "response - request fragments"); + p2p_dbg(p2p, "Fragmented response - request fragments"); if (p2p->sd_rx_resp) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " - "old SD reassembly buffer"); + p2p_dbg(p2p, "Drop old SD reassembly buffer"); wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; } @@ -544,8 +555,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; @@ -553,41 +563,29 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Response length"); + p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } - if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x", + WPA_GET_BE32(pos)); return; } - pos += 3; - - if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); - return; - } - pos++; + pos += 4; if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; - p2p->sd_peer->flags |= P2P_DEV_SD_INFO; - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove completed SD query %p", + p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); @@ -615,22 +613,20 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, if (len < 1) return; dialog_token = *data; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u", - dialog_token); + p2p_dbg(p2p, "Dialog Token: %u", dialog_token); if (dialog_token != p2p->sd_resp_dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment for dialog token %u", dialog_token); + p2p_dbg(p2p, "No pending SD response fragment for dialog token %u", + dialog_token); return; } if (p2p->sd_resp == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment available"); + p2p_dbg(p2p, "No pending SD response fragment available"); return; } if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment for " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "No pending SD response fragment for " MACSTR, + MAC2STR(sa)); return; } @@ -647,19 +643,16 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, wpabuf_len(p2p->sd_resp)); if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback " - "Response (frag_id %d more=%d frag_len=%d)", + p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)", p2p->sd_frag_id, more, (int) frag_len); p2p->sd_frag_id++; p2p->sd_resp_pos += frag_len; if (more) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes " - "remain to be sent", + p2p_dbg(p2p, "%d more bytes remain to be sent", (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of " - "SD response sent"); + p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; } @@ -668,8 +661,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } @@ -692,21 +684,18 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected GAS Comeback Response from " + p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)", + p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 6 + 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Too short GAS Comeback Response frame"); + p2p_dbg(p2p, "Too short GAS Comeback Response frame"); return; } @@ -719,22 +708,19 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos++; comeback_delay = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d " + p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d " "comeback_delay=%u", dialog_token, status_code, frag_id, more_frags, comeback_delay); /* TODO: check frag_id match */ if (status_code) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery failed: status code %u", + p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Comeback Response: %u", + p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u", *pos); return; } @@ -743,15 +729,13 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Comeback Response"); + p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -759,22 +743,18 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ if (pos + 2 > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " - "Response"); + p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", - slen); + p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " - "Response data"); + p2p_dbg(p2p, "Not enough Query Response data"); return; } if (slen == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response " - "data"); + p2p_dbg(p2p, "No Query Response data"); return; } end = pos + slen; @@ -791,77 +771,60 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response " - "length: %u", slen); + p2p_dbg(p2p, "ANQP Query Response length: %u", slen); if (slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Response length"); + p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } if (pos + 4 > end) return; - if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x", + WPA_GET_BE32(pos)); return; } - pos += 3; - - if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); - return; - } - pos++; + pos += 4; if (pos + 2 > end) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); pos += 2; skip_nqp_header: if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) return; wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly " - "buffer length: %u", + p2p_dbg(p2p, "Current SD reassembly buffer length: %u", (unsigned int) wpabuf_len(p2p->sd_rx_resp)); if (more_frags) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments " - "remains"); + p2p_dbg(p2p, "More fragments remains"); /* TODO: what would be a good size limit? */ if (wpabuf_len(p2p->sd_rx_resp) > 64000) { wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long " - "SD response - drop it"); + p2p_dbg(p2p, "Too long SD response - drop it"); return; } p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); return; } - p2p->sd_peer->flags |= P2P_DEV_SD_INFO; - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove completed SD query %p", + p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); @@ -904,12 +867,20 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, q->next = p2p->sd_queries; p2p->sd_queries = q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q); + p2p_dbg(p2p, "Added SD Query %p", q); if (dst == NULL) { struct p2p_device *dev; - dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) - dev->flags &= ~P2P_DEV_SD_INFO; + + p2p->num_p2p_sd_queries++; + + /* Update all the devices for the newly added broadcast query */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (dev->sd_pending_bcast_queries <= 0) + dev->sd_pending_bcast_queries = 1; + else + dev->sd_pending_bcast_queries++; + } } return q; @@ -938,8 +909,7 @@ void p2p_sd_service_update(struct p2p_data *p2p) int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) { if (p2p_unlink_sd_query(p2p, req)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cancel pending SD query %p", req); + p2p_dbg(p2p, "Cancel pending SD query %p", req); p2p_free_sd_query(req); return 0; } diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index bcc690d8469b..f32751d79ca8 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "common/ieee802_11_common.h" #include "p2p_i.h" @@ -46,127 +47,69 @@ int p2p_random(char *buf, size_t len) } -static int p2p_channel_to_freq_j4(int reg_class, int channel) -{ - /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ - /* TODO: more regulatory classes */ - switch (reg_class) { - case 81: - /* channels 1..13 */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 82: - /* channel 14 */ - if (channel != 14) - return -1; - return 2414 + 5 * channel; - case 83: /* channels 1..9; 40 MHz */ - case 84: /* channels 5..13; 40 MHz */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 115: /* channels 36,40,44,48; indoor only */ - case 118: /* channels 52,56,60,64; dfs */ - if (channel < 36 || channel > 64) - return -1; - return 5000 + 5 * channel; - case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ - if (channel < 149 || channel > 161) - return -1; - return 5000 + 5 * channel; - case 116: /* channels 36,44; 40 MHz; indoor only */ - case 117: /* channels 40,48; 40 MHz; indoor only */ - case 119: /* channels 52,60; 40 MHz; dfs */ - case 120: /* channels 56,64; 40 MHz; dfs */ - if (channel < 36 || channel > 64) - return -1; - return 5000 + 5 * channel; - case 126: /* channels 149,157; 40 MHz */ - case 127: /* channels 153,161; 40 MHz */ - if (channel < 149 || channel > 161) - return -1; - return 5000 + 5 * channel; - } - return -1; -} - - /** * p2p_channel_to_freq - Convert channel info to frequency - * @country: Country code - * @reg_class: Regulatory class + * @op_class: Operating class * @channel: Channel number * Returns: Frequency in MHz or -1 if the specified channel is unknown */ -int p2p_channel_to_freq(const char *country, int reg_class, int channel) +int p2p_channel_to_freq(int op_class, int channel) { - if (country[2] == 0x04) - return p2p_channel_to_freq_j4(reg_class, channel); - - /* These are mainly for backwards compatibility; to be removed */ - switch (reg_class) { - case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */ - if (channel < 36 || channel > 48) - return -1; - return 5000 + 5 * channel; - case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */ - case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */ - if (channel < 149 || channel > 161) - return -1; - return 5000 + 5 * channel; - case 4: /* EU/4 = 2.407 GHz, channels 1..13 */ - case 12: /* US/12 = 2.407 GHz, channels 1..11 */ - case 30: /* JP/30 = 2.407 GHz, channels 1..13 */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 31: /* JP/31 = 2.414 GHz, channel 14 */ - if (channel != 14) - return -1; - return 2414 + 5 * channel; - } - - return -1; + return ieee80211_chan_to_freq(NULL, op_class, channel); } /** * p2p_freq_to_channel - Convert frequency into channel info - * @country: Country code - * @reg_class: Buffer for returning regulatory class + * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: 0 on success, -1 if the specified frequency is unknown */ -int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, - u8 *channel) +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { /* TODO: more operating classes */ if (freq >= 2412 && freq <= 2472) { - *reg_class = 81; /* 2.407 GHz, channels 1..13 */ + if ((freq - 2407) % 5) + return -1; + + *op_class = 81; /* 2.407 GHz, channels 1..13 */ *channel = (freq - 2407) / 5; return 0; } if (freq == 2484) { - *reg_class = 82; /* channel 14 */ + *op_class = 82; /* channel 14 */ *channel = 14; return 0; } if (freq >= 5180 && freq <= 5240) { - *reg_class = 115; /* 5 GHz, channels 36..48 */ + if ((freq - 5000) % 5) + return -1; + + *op_class = 115; /* 5 GHz, channels 36..48 */ *channel = (freq - 5000) / 5; return 0; } if (freq >= 5745 && freq <= 5805) { - *reg_class = 124; /* 5 GHz, channels 149..161 */ + if ((freq - 5000) % 5) + return -1; + + *op_class = 124; /* 5 GHz, channels 149..161 */ *channel = (freq - 5000) / 5; return 0; } + if (freq >= 58320 && freq <= 64800) { + if ((freq - 58320) % 2160) + return -1; + + *op_class = 180; /* 60 GHz, channels 1..4 */ + *channel = (freq - 56160) / 2160; + return 0; + } + return -1; } @@ -230,6 +173,115 @@ void p2p_channels_intersect(const struct p2p_channels *a, } +static void p2p_op_class_union(struct p2p_reg_class *cl, + const struct p2p_reg_class *b_cl) +{ + size_t i, j; + + for (i = 0; i < b_cl->channels; i++) { + for (j = 0; j < cl->channels; j++) { + if (b_cl->channel[i] == cl->channel[j]) + break; + } + if (j == cl->channels) { + if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + cl->channel[cl->channels++] = b_cl->channel[i]; + } + } +} + + +/** + * p2p_channels_union_inplace - Inplace union of channel lists + * @res: Input data and place for returning union of the channel sets + * @b: Second set of channels + */ +void p2p_channels_union_inplace(struct p2p_channels *res, + const struct p2p_channels *b) +{ + size_t i, j; + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + if (cl->reg_class != b_cl->reg_class) + continue; + p2p_op_class_union(cl, b_cl); + } + } + + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + if (cl->reg_class == b_cl->reg_class) + break; + } + + if (i == res->reg_classes) { + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + os_memcpy(&res->reg_class[res->reg_classes++], + b_cl, sizeof(struct p2p_reg_class)); + } + } +} + + +/** + * p2p_channels_union - Union of channel lists + * @a: First set of channels + * @b: Second set of channels + * @res: Data structure for returning the union of channels + */ +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + os_memcpy(res, a, sizeof(*res)); + p2p_channels_union_inplace(res, b); +} + + +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list) +{ + size_t o, c; + + if (list == NULL) + return; + + o = 0; + while (o < chan->reg_classes) { + struct p2p_reg_class *op = &chan->reg_class[o]; + + c = 0; + while (c < op->channels) { + int freq = p2p_channel_to_freq(op->reg_class, + op->channel[c]); + if (freq > 0 && freq_range_list_includes(list, freq)) { + op->channels--; + os_memmove(&op->channel[c], + &op->channel[c + 1], + op->channels - c); + } else + c++; + } + + if (op->channels == 0) { + chan->reg_classes--; + os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], + (chan->reg_classes - o) * + sizeof(struct p2p_reg_class)); + } else + o++; + } +} + + /** * p2p_channels_includes - Check whether a channel is included in the list * @channels: List of supported channels @@ -254,12 +306,208 @@ int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, } +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + for (j = 0; j < reg->channels; j++) { + if (p2p_channel_to_freq(reg->reg_class, + reg->channel[j]) == (int) freq) + return 1; + } + } + return 0; +} + + int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; - if (p2p_freq_to_channel(p2p->cfg->country, freq, - &op_reg_class, &op_channel) < 0) + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel); } + + +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) && + !freq_range_list_includes(&p2p->no_go_freq, freq); +} + + +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) || + p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class, + op_channel); +} + + +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels) +{ + unsigned int i; + int freq = 0; + const struct p2p_channels *tmpc = channels ? + channels : &p2p->cfg->channels; + + if (tmpc == NULL) + return 0; + + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan); + if (p2p_channels_includes_freq(tmpc, freq)) + return freq; + } + return 0; +} + + +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan) +{ + char buf[500], *pos, *end; + size_t i, j; + int ret; + + pos = buf; + end = pos + sizeof(buf); + + for (i = 0; i < chan->reg_classes; i++) { + const struct p2p_reg_class *c; + c = &chan->reg_class[i]; + ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + + for (j = 0; j < c->channels; j++) { + ret = os_snprintf(pos, end - pos, "%s%u", + j == 0 ? "" : ",", + c->channel[j]); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + } + *pos = '\0'; + + p2p_dbg(p2p, "%s:%s", title, buf); +} + + +static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels) +{ + unsigned int r; + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; + r %= num_channels; + return channels[r]; +} + + +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel) +{ + unsigned int i, j; + + for (j = 0; classes == NULL || classes[j]; j++) { + for (i = 0; i < chans->reg_classes; i++) { + struct p2p_reg_class *c = &chans->reg_class[i]; + + if (c->channels == 0) + continue; + + if (classes == NULL || c->reg_class == classes[j]) { + /* + * Pick one of the available channels in the + * operating class at random. + */ + *op_class = c->reg_class; + *op_channel = p2p_channel_pick_random( + c->channel, c->channels); + return 0; + } + } + if (classes == NULL) + break; + } + + return -1; +} + + +int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, + u8 *op_channel) +{ + u8 chan[4]; + unsigned int num_channels = 0; + + /* Try to find available social channels from 2.4 GHz */ + if (p2p_channels_includes(chans, 81, 1)) + chan[num_channels++] = 1; + if (p2p_channels_includes(chans, 81, 6)) + chan[num_channels++] = 6; + if (p2p_channels_includes(chans, 81, 11)) + chan[num_channels++] = 11; + + /* Try to find available social channels from 60 GHz */ + if (p2p_channels_includes(chans, 180, 2)) + chan[num_channels++] = 2; + + if (num_channels == 0) + return -1; + + *op_channel = p2p_channel_pick_random(chan, num_channels); + if (*op_channel == 2) + *op_class = 180; + else + *op_class = 81; + + return 0; +} + + +int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, + unsigned int max_len) +{ + unsigned int i, idx; + + if (!channels || max_len == 0) + return 0; + + for (i = 0, idx = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *c = &channels->reg_class[i]; + unsigned int j; + + if (idx + 1 == max_len) + break; + for (j = 0; j < c->channels; j++) { + int freq; + if (idx + 1 == max_len) + break; + freq = p2p_channel_to_freq(c->reg_class, + c->channel[j]); + if (freq < 0) + continue; + freq_list[idx++] = freq; + } + } + + freq_list[idx] = 0; + + return idx; +} diff --git a/src/pae/Makefile b/src/pae/Makefile new file mode 100644 index 000000000000..9c41962fd7e1 --- /dev/null +++ b/src/pae/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c new file mode 100644 index 000000000000..cf43c594c402 --- /dev/null +++ b/src/pae/ieee802_1x_cp.c @@ -0,0 +1,744 @@ +/* + * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine + * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. + * + * 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/defs.h" +#include "common/ieee802_1x_defs.h" +#include "utils/state_machine.h" +#include "ieee802_1x_kay.h" +#include "ieee802_1x_secy_ops.h" +#include "pae/ieee802_1x_cp.h" + +#define STATE_MACHINE_DATA struct ieee802_1x_cp_sm +#define STATE_MACHINE_DEBUG_PREFIX "CP" + +static u8 default_cs_id[] = CS_ID_GCM_AES_128; + +/* The variable defined in clause 12 in IEEE Std 802.1X-2010 */ +enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE }; + +struct ieee802_1x_cp_sm { + enum cp_states { + CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED, + CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT, + CP_TRANSMITTING, CP_ABANDON, CP_RETIRE + } CP_state; + Boolean changed; + + /* CP -> Client */ + Boolean port_valid; + + /* Logon -> CP */ + enum connect_type connect; + u8 *authorization_data; + + /* KaY -> CP */ + Boolean chgd_server; /* clear by CP */ + Boolean elected_self; + u8 *authorization_data1; + enum confidentiality_offset cipher_offset; + u8 *cipher_suite; + Boolean new_sak; /* clear by CP */ + struct ieee802_1x_mka_ki distributed_ki; + u8 distributed_an; + Boolean using_receive_sas; + Boolean all_receiving; + Boolean server_transmitting; + Boolean using_transmit_sa; + + /* CP -> KaY */ + struct ieee802_1x_mka_ki *lki; + u8 lan; + Boolean ltx; + Boolean lrx; + struct ieee802_1x_mka_ki *oki; + u8 oan; + Boolean otx; + Boolean orx; + + /* CP -> SecY */ + Boolean protect_frames; + enum validate_frames validate_frames; + + Boolean replay_protect; + u32 replay_window; + + u8 *current_cipher_suite; + enum confidentiality_offset confidentiality_offset; + Boolean controlled_port_enabled; + + /* SecY -> CP */ + Boolean port_enabled; /* SecY->CP */ + + /* private */ + u32 transmit_when; + u32 transmit_delay; + u32 retire_when; + u32 retire_delay; + + /* not defined IEEE Std 802.1X-2010 */ + struct ieee802_1x_kay *kay; +}; + +static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx, + void *timeout_ctx); +static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, + void *timeout_ctx); + + +static int changed_cipher(struct ieee802_1x_cp_sm *sm) +{ + return sm->confidentiality_offset != sm->cipher_offset || + os_memcmp(sm->current_cipher_suite, sm->cipher_suite, + CS_ID_LEN) != 0; +} + + +static int changed_connect(struct ieee802_1x_cp_sm *sm) +{ + return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm); +} + + +SM_STATE(CP, INIT) +{ + SM_ENTRY(CP, INIT); + + sm->controlled_port_enabled = FALSE; + secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); + + sm->port_valid = FALSE; + + os_free(sm->lki); + sm->lki = NULL; + sm->ltx = FALSE; + sm->lrx = FALSE; + + os_free(sm->oki); + sm->oki = NULL; + sm->otx = FALSE; + sm->orx = FALSE; + + sm->port_enabled = TRUE; + sm->chgd_server = FALSE; +} + + +SM_STATE(CP, CHANGE) +{ + SM_ENTRY(CP, CHANGE); + + sm->port_valid = FALSE; + sm->controlled_port_enabled = FALSE; + secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); + + if (sm->lki) + ieee802_1x_kay_delete_sas(sm->kay, sm->lki); + if (sm->oki) + ieee802_1x_kay_delete_sas(sm->kay, sm->oki); +} + + +SM_STATE(CP, ALLOWED) +{ + SM_ENTRY(CP, ALLOWED); + + sm->protect_frames = FALSE; + sm->replay_protect = FALSE; + sm->validate_frames = Checked; + + sm->port_valid = FALSE; + sm->controlled_port_enabled = TRUE; + + secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); + secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_validate_frames(sm->kay, sm->validate_frames); + secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); +} + + +SM_STATE(CP, AUTHENTICATED) +{ + SM_ENTRY(CP, AUTHENTICATED); + + sm->protect_frames = FALSE; + sm->replay_protect = FALSE; + sm->validate_frames = Checked; + + sm->port_valid = FALSE; + sm->controlled_port_enabled = TRUE; + + secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); + secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_validate_frames(sm->kay, sm->validate_frames); + secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); +} + + +SM_STATE(CP, SECURED) +{ + struct ieee802_1x_cp_conf conf; + + SM_ENTRY(CP, SECURED); + + sm->chgd_server = FALSE; + + ieee802_1x_kay_cp_conf(sm->kay, &conf); + sm->protect_frames = conf.protect; + sm->replay_protect = conf.replay_protect; + sm->validate_frames = conf.validate; + + /* NOTE: now no other than default cipher suiter(AES-GCM-128) */ + os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN); + secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite, + CS_ID_LEN); + + sm->confidentiality_offset = sm->cipher_offset; + + sm->port_valid = TRUE; + + secy_cp_control_confidentiality_offset(sm->kay, + sm->confidentiality_offset); + secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_validate_frames(sm->kay, sm->validate_frames); + secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); +} + + +SM_STATE(CP, RECEIVE) +{ + SM_ENTRY(CP, RECEIVE); + /* RECEIVE state machine not keep with Figure 12-2 in + * IEEE Std 802.1X-2010 */ + sm->oki = sm->lki; + sm->oan = sm->lan; + sm->otx = sm->ltx; + sm->orx = sm->lrx; + ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, + sm->otx, sm->orx); + + sm->lki = os_malloc(sizeof(*sm->lki)); + if (!sm->lki) { + wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__); + return; + } + os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki)); + sm->lan = sm->distributed_an; + sm->ltx = FALSE; + sm->lrx = FALSE; + ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, + sm->ltx, sm->lrx); + ieee802_1x_kay_create_sas(sm->kay, sm->lki); + ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki); + sm->new_sak = FALSE; + sm->all_receiving = FALSE; +} + + +SM_STATE(CP, RECEIVING) +{ + SM_ENTRY(CP, RECEIVING); + + sm->lrx = TRUE; + ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, + sm->ltx, sm->lrx); + sm->transmit_when = sm->transmit_delay; + eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL); + eloop_register_timeout(sm->transmit_when / 1000, 0, + ieee802_1x_cp_transmit_when_timeout, sm, NULL); + /* the electedSelf have been set before CP entering to RECEIVING + * but the CP will transmit from RECEIVING to READY under + * the !electedSelf when KaY is not key server */ + ieee802_1x_cp_sm_step(sm); + sm->using_receive_sas = FALSE; + sm->server_transmitting = FALSE; +} + + +SM_STATE(CP, READY) +{ + SM_ENTRY(CP, READY); + + ieee802_1x_kay_enable_new_info(sm->kay); +} + + +SM_STATE(CP, TRANSMIT) +{ + SM_ENTRY(CP, TRANSMIT); + + sm->controlled_port_enabled = TRUE; + secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); + sm->ltx = TRUE; + ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, + sm->ltx, sm->lrx); + ieee802_1x_kay_enable_tx_sas(sm->kay, sm->lki); + sm->all_receiving = FALSE; + sm->server_transmitting = FALSE; +} + + +SM_STATE(CP, TRANSMITTING) +{ + SM_ENTRY(CP, TRANSMITTING); + sm->retire_when = sm->orx ? sm->retire_delay : 0; + sm->otx = FALSE; + ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, + sm->otx, sm->orx); + ieee802_1x_kay_enable_new_info(sm->kay); + eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL); + eloop_register_timeout(sm->retire_when / 1000, 0, + ieee802_1x_cp_retire_when_timeout, sm, NULL); + sm->using_transmit_sa = FALSE; +} + + +SM_STATE(CP, ABANDON) +{ + SM_ENTRY(CP, ABANDON); + sm->lrx = FALSE; + ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, + sm->ltx, sm->lrx); + ieee802_1x_kay_delete_sas(sm->kay, sm->lki); + + os_free(sm->lki); + sm->lki = NULL; + ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, + sm->ltx, sm->lrx); + sm->new_sak = FALSE; +} + + +SM_STATE(CP, RETIRE) +{ + SM_ENTRY(CP, RETIRE); + /* RETIRE state machine not keep with Figure 12-2 in + * IEEE Std 802.1X-2010 */ + os_free(sm->oki); + sm->oki = NULL; + sm->orx = FALSE; + sm->otx = FALSE; + ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, + sm->otx, sm->orx); +} + + +/** + * CP state machine handler entry + */ +SM_STEP(CP) +{ + if (!sm->port_enabled) + SM_ENTER(CP, INIT); + + switch (sm->CP_state) { + case CP_BEGIN: + SM_ENTER(CP, INIT); + break; + + case CP_INIT: + SM_ENTER(CP, CHANGE); + break; + + case CP_CHANGE: + if (sm->connect == UNAUTHENTICATED) + SM_ENTER(CP, ALLOWED); + else if (sm->connect == AUTHENTICATED) + SM_ENTER(CP, AUTHENTICATED); + else if (sm->connect == SECURE) + SM_ENTER(CP, SECURED); + break; + + case CP_ALLOWED: + if (sm->connect != UNAUTHENTICATED) + SM_ENTER(CP, CHANGE); + break; + + case CP_AUTHENTICATED: + if (sm->connect != AUTHENTICATED) + SM_ENTER(CP, CHANGE); + break; + + case CP_SECURED: + if (changed_connect(sm)) + SM_ENTER(CP, CHANGE); + else if (sm->new_sak) + SM_ENTER(CP, RECEIVE); + break; + + case CP_RECEIVE: + if (sm->using_receive_sas) + SM_ENTER(CP, RECEIVING); + break; + + case CP_RECEIVING: + if (sm->new_sak || changed_connect(sm)) + SM_ENTER(CP, ABANDON); + if (!sm->elected_self) + SM_ENTER(CP, READY); + if (sm->elected_self && + (sm->all_receiving || !sm->transmit_when)) + SM_ENTER(CP, TRANSMIT); + break; + + case CP_TRANSMIT: + if (sm->using_transmit_sa) + SM_ENTER(CP, TRANSMITTING); + break; + + case CP_TRANSMITTING: + if (!sm->retire_when || changed_connect(sm)) + SM_ENTER(CP, RETIRE); + break; + + case CP_RETIRE: + if (changed_connect(sm)) + SM_ENTER(CP, CHANGE); + else if (sm->new_sak) + SM_ENTER(CP, RECEIVE); + break; + + case CP_READY: + if (sm->new_sak || changed_connect(sm)) + SM_ENTER(CP, RECEIVE); + if (sm->server_transmitting) + SM_ENTER(CP, TRANSMIT); + break; + case CP_ABANDON: + if (changed_connect(sm)) + SM_ENTER(CP, RETIRE); + else if (sm->new_sak) + SM_ENTER(CP, RECEIVE); + break; + default: + wpa_printf(MSG_ERROR, "CP: the state machine is not defined"); + break; + } +} + + +/** + * ieee802_1x_cp_sm_init - + */ +struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( + struct ieee802_1x_kay *kay, + struct ieee802_1x_cp_conf *pcp_conf) +{ + struct ieee802_1x_cp_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) { + wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__); + return NULL; + } + + sm->kay = kay; + + sm->port_valid = FALSE; + + sm->chgd_server = FALSE; + + sm->protect_frames = pcp_conf->protect; + sm->validate_frames = pcp_conf->validate; + sm->replay_protect = pcp_conf->replay_protect; + sm->replay_window = pcp_conf->replay_window; + + sm->controlled_port_enabled = FALSE; + + sm->lki = NULL; + sm->lrx = FALSE; + sm->ltx = FALSE; + sm->oki = NULL; + sm->orx = FALSE; + sm->otx = FALSE; + + sm->cipher_suite = os_zalloc(CS_ID_LEN); + sm->current_cipher_suite = os_zalloc(CS_ID_LEN); + if (!sm->cipher_suite || !sm->current_cipher_suite) { + wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__); + os_free(sm->cipher_suite); + os_free(sm->current_cipher_suite); + os_free(sm); + return NULL; + } + os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN); + os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN); + sm->cipher_offset = CONFIDENTIALITY_OFFSET_0; + sm->confidentiality_offset = sm->cipher_offset; + sm->transmit_delay = MKA_LIFE_TIME; + sm->retire_delay = MKA_SAK_RETIRE_TIME; + sm->CP_state = CP_BEGIN; + sm->changed = FALSE; + sm->authorization_data = NULL; + + wpa_printf(MSG_DEBUG, "CP: state machine created"); + + secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_validate_frames(sm->kay, sm->validate_frames); + secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); + secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); + secy_cp_control_confidentiality_offset(sm->kay, + sm->confidentiality_offset); + + SM_ENTER(CP, INIT); + SM_STEP_RUN(CP); + + return sm; +} + + +static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm) +{ + enum cp_states prev_state; + int i; + + for (i = 0; i < 100; i++) { + prev_state = sm->CP_state; + SM_STEP_RUN(CP); + if (prev_state == sm->CP_state) + break; + } +} + + +static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct ieee802_1x_cp_sm *sm = eloop_ctx; + ieee802_1x_cp_step_run(sm); +} + + +/** + * ieee802_1x_cp_sm_deinit - + */ +void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm) +{ + wpa_printf(MSG_DEBUG, "CP: state machine removed"); + if (!sm) + return; + + eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL); + eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL); + eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); + os_free(sm->lki); + os_free(sm->oki); + os_free(sm->cipher_suite); + os_free(sm->current_cipher_suite); + os_free(sm->authorization_data); + os_free(sm); +} + + +/** + * ieee802_1x_cp_connect_pending + */ +void ieee802_1x_cp_connect_pending(void *cp_ctx) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + + sm->connect = PENDING; +} + + +/** + * ieee802_1x_cp_connect_unauthenticated + */ +void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx) +{ + struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx; + + sm->connect = UNAUTHENTICATED; +} + + +/** + * ieee802_1x_cp_connect_authenticated + */ +void ieee802_1x_cp_connect_authenticated(void *cp_ctx) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + + sm->connect = AUTHENTICATED; +} + + +/** + * ieee802_1x_cp_connect_secure + */ +void ieee802_1x_cp_connect_secure(void *cp_ctx) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + + sm->connect = SECURE; +} + + +/** + * ieee802_1x_cp_set_chgdserver - + */ +void ieee802_1x_cp_signal_chgdserver(void *cp_ctx) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + + sm->chgd_server = TRUE; +} + + +/** + * ieee802_1x_cp_set_electedself - + */ +void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->elected_self = status; +} + + +/** + * ieee802_1x_cp_set_authorizationdata - + */ +void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + os_free(sm->authorization_data); + sm->authorization_data = os_zalloc(len); + if (sm->authorization_data) + os_memcpy(sm->authorization_data, pdata, len); +} + + +/** + * ieee802_1x_cp_set_ciphersuite - + */ +void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + os_memcpy(sm->cipher_suite, pid, CS_ID_LEN); +} + + +/** + * ieee802_1x_cp_set_offset - + */ +void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->cipher_offset = offset; +} + + +/** + * ieee802_1x_cp_signal_newsak - + */ +void ieee802_1x_cp_signal_newsak(void *cp_ctx) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->new_sak = TRUE; +} + + +/** + * ieee802_1x_cp_set_distributedki - + */ +void ieee802_1x_cp_set_distributedki(void *cp_ctx, + const struct ieee802_1x_mka_ki *dki) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki)); +} + + +/** + * ieee802_1x_cp_set_distributedan - + */ +void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->distributed_an = an; +} + + +/** + * ieee802_1x_cp_set_usingreceivesas - + */ +void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->using_receive_sas = status; +} + + +/** + * ieee802_1x_cp_set_allreceiving - + */ +void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->all_receiving = status; +} + + +/** + * ieee802_1x_cp_set_servertransmitting - + */ +void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->server_transmitting = status; +} + + +/** + * ieee802_1x_cp_set_usingtransmitsas - + */ +void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status) +{ + struct ieee802_1x_cp_sm *sm = cp_ctx; + sm->using_transmit_sa = status; +} + + +/** + * ieee802_1x_cp_sm_step - Advance EAPOL state machines + * @sm: EAPOL state machine + * + * This function is called to advance CP state machines after any change + * that could affect their state. + */ +void ieee802_1x_cp_sm_step(void *cp_ctx) +{ + /* + * Run ieee802_1x_cp_step_run from a registered timeout + * to make sure that other possible timeouts/events are processed + * and to avoid long function call chains. + */ + struct ieee802_1x_cp_sm *sm = cp_ctx; + eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); + eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL); +} + + +static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct ieee802_1x_cp_sm *sm = eloop_ctx; + sm->retire_when = 0; + ieee802_1x_cp_step_run(sm); +} + + +static void +ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ieee802_1x_cp_sm *sm = eloop_ctx; + sm->transmit_when = 0; + ieee802_1x_cp_step_run(sm); +} diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h new file mode 100644 index 000000000000..773c93052bf6 --- /dev/null +++ b/src/pae/ieee802_1x_cp.h @@ -0,0 +1,50 @@ +/* + * IEEE Std 802.1X-2010 Controlled Port of PAE state machine - CP state machine + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_CP_H +#define IEEE802_1X_CP_H + +#include "common/defs.h" +#include "common/ieee802_1x_defs.h" + +struct ieee802_1x_cp_sm; +struct ieee802_1x_kay; +struct ieee802_1x_mka_ki; + +struct ieee802_1x_cp_conf { + Boolean protect; + Boolean replay_protect; + enum validate_frames validate; + u32 replay_window; +}; + + +struct ieee802_1x_cp_sm * +ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay, + struct ieee802_1x_cp_conf *pcp_conf); +void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm); +void ieee802_1x_cp_sm_step(void *cp_ctx); +void ieee802_1x_cp_connect_pending(void *cp_ctx); +void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx); +void ieee802_1x_cp_connect_authenticated(void *cp_ctx); +void ieee802_1x_cp_connect_secure(void *cp_ctx); +void ieee802_1x_cp_signal_chgdserver(void *cp_ctx); +void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status); +void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len); +void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid); +void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset); +void ieee802_1x_cp_signal_newsak(void *cp_ctx); +void ieee802_1x_cp_set_distributedki(void *cp_ctx, + const struct ieee802_1x_mka_ki *dki); +void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an); +void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status); +void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status); +void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status); +void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status); + +#endif /* IEEE802_1X_CP_H */ diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c new file mode 100644 index 000000000000..ef744304a2bb --- /dev/null +++ b/src/pae/ieee802_1x_kay.c @@ -0,0 +1,3541 @@ +/* + * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include +#include "includes.h" +#include "common.h" +#include "list.h" +#include "eloop.h" +#include "wpabuf.h" +#include "state_machine.h" +#include "l2_packet/l2_packet.h" +#include "common/eapol_common.h" +#include "crypto/aes_wrap.h" +#include "ieee802_1x_cp.h" +#include "ieee802_1x_key.h" +#include "ieee802_1x_kay.h" +#include "ieee802_1x_kay_i.h" +#include "ieee802_1x_secy_ops.h" + + +#define DEFAULT_SA_KEY_LEN 16 +#define DEFAULT_ICV_LEN 16 +#define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */ + +#define PENDING_PN_EXHAUSTION 0xC0000000 + +/* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */ +#define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 } +static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009; + +/* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */ +static struct macsec_ciphersuite cipher_suite_tbl[] = { + /* GCM-AES-128 */ + { + CS_ID_GCM_AES_128, + CS_NAME_GCM_AES_128, + MACSEC_CAP_INTEG_AND_CONF_0_30_50, + 16, + + 0 /* index */ + }, +}; +#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) +#define DEFAULT_CS_INDEX 0 + +static struct mka_alg mka_alg_tbl[] = { + { + MKA_ALGO_AGILITY_2009, + /* 128-bit CAK, KEK, ICK, ICV */ + 16, 16, 16, 16, + ieee802_1x_cak_128bits_aes_cmac, + ieee802_1x_ckn_128bits_aes_cmac, + ieee802_1x_kek_128bits_aes_cmac, + ieee802_1x_ick_128bits_aes_cmac, + ieee802_1x_icv_128bits_aes_cmac, + + 1, /* index */ + }, +}; +#define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl)) + + +static int is_ki_equal(struct ieee802_1x_mka_ki *ki1, + struct ieee802_1x_mka_ki *ki2) +{ + return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 && + ki1->kn == ki2->kn; +} + + +struct mka_param_body_handler { + int (*body_tx)(struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf); + int (*body_rx)(struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len); + int (*body_length)(struct ieee802_1x_mka_participant *participant); + Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); +}; + + +static void set_mka_param_body_len(void *body, unsigned int len) +{ + struct ieee802_1x_mka_hdr *hdr = body; + hdr->length = (len >> 8) & 0x0f; + hdr->length1 = len & 0xff; +} + + +static unsigned int get_mka_param_body_len(const void *body) +{ + const struct ieee802_1x_mka_hdr *hdr = body; + return (hdr->length << 8) | hdr->length1; +} + + +static int get_mka_param_body_type(const void *body) +{ + const struct ieee802_1x_mka_hdr *hdr = body; + return hdr->type; +} + + +/** + * ieee802_1x_mka_dump_basic_body - + */ +static void +ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body) +{ + size_t body_len; + + if (!body) + return; + + body_len = get_mka_param_body_len(body); + wpa_printf(MSG_DEBUG, "*** MKA Basic Parameter set ***"); + wpa_printf(MSG_DEBUG, "\tVersion.......: %d", body->version); + wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority); + wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server); + wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired); + wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capbility); + wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR, + MAC2STR(body->actor_sci.addr)); + wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d", + be_to_host16(body->actor_sci.port)); + wpa_hexdump(MSG_DEBUG, "\tMember Id.....:", + body->actor_mi, sizeof(body->actor_mi)); + wpa_printf(MSG_DEBUG, "\tMessage Number: %d", + be_to_host32(body->actor_mn)); + wpa_hexdump(MSG_DEBUG, "\tAlgo Agility..:", + body->algo_agility, sizeof(body->algo_agility)); + wpa_hexdump_ascii(MSG_DEBUG, "\tCAK Name......:", body->ckn, + body_len + MKA_HDR_LEN - sizeof(*body)); +} + + +/** + * ieee802_1x_mka_dump_peer_body - + */ +static void +ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) +{ + size_t body_len; + size_t i; + u8 *mi; + u32 mn; + + if (body == NULL) + return; + + body_len = get_mka_param_body_len(body); + if (body->type == MKA_LIVE_PEER_LIST) { + wpa_printf(MSG_DEBUG, "*** Live Peer List ***"); + wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + } else if (body->type == MKA_POTENTIAL_PEER_LIST) { + wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***"); + wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + } + + for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) { + mi = body->peer + i; + os_memcpy(&mn, mi + MI_LEN, sizeof(mn)); + wpa_hexdump_ascii(MSG_DEBUG, "\tMember Id.....:", mi, MI_LEN); + wpa_printf(MSG_DEBUG, "\tMessage Number: %d", be_to_host32(mn)); + } +} + + +/** + * ieee802_1x_mka_dump_dist_sak_body - + */ +static void +ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body) +{ + size_t body_len; + + if (body == NULL) + return; + + body_len = get_mka_param_body_len(body); + wpa_printf(MSG_INFO, "*** Distributed SAK ***"); + wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan); + wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d", + body->confid_offset); + wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len); + if (!body_len) + return; + + wpa_printf(MSG_INFO, "\tKey Number............: %d", + be_to_host32(body->kn)); + wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24); +} + + +static const char * yes_no(int val) +{ + return val ? "Yes" : "No"; +} + + +/** + * ieee802_1x_mka_dump_sak_use_body - + */ +static void +ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body) +{ + int body_len; + + if (body == NULL) + return; + + body_len = get_mka_param_body_len(body); + wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***"); + wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan); + wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx)); + wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx)); + wpa_printf(MSG_DEBUG, "\tOld Key AN....: %d", body->oan); + wpa_printf(MSG_DEBUG, "\tOld Key Tx....: %s", yes_no(body->otx)); + wpa_printf(MSG_DEBUG, "\tOld Key Rx....: %s", yes_no(body->orx)); + wpa_printf(MSG_DEBUG, "\tPlain Key Tx....: %s", yes_no(body->ptx)); + wpa_printf(MSG_DEBUG, "\tPlain Key Rx....: %s", yes_no(body->prx)); + wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s", + yes_no(body->delay_protect)); + wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len); + if (!body_len) + return; + + wpa_hexdump(MSG_DEBUG, "\tKey Server MI....:", + body->lsrv_mi, sizeof(body->lsrv_mi)); + wpa_printf(MSG_DEBUG, "\tKey Number.......: %u", + be_to_host32(body->lkn)); + wpa_printf(MSG_DEBUG, "\tLowest PN........: %u", + be_to_host32(body->llpn)); + wpa_hexdump_ascii(MSG_DEBUG, "\tOld Key Server MI....:", + body->osrv_mi, sizeof(body->osrv_mi)); + wpa_printf(MSG_DEBUG, "\tOld Key Number.......: %u", + be_to_host32(body->okn)); + wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u", + be_to_host32(body->olpn)); +} + + +/** + * ieee802_1x_kay_get_participant - + */ +static struct ieee802_1x_mka_participant * +ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn) +{ + struct ieee802_1x_mka_participant *participant; + + dl_list_for_each(participant, &kay->participant_list, + struct ieee802_1x_mka_participant, list) { + if (os_memcmp(participant->ckn.name, ckn, + participant->ckn.len) == 0) + return participant; + } + + wpa_printf(MSG_DEBUG, "KaY: participant is not found"); + + return NULL; +} + + +/** + * ieee802_1x_kay_get_principal_participant - + */ +static struct ieee802_1x_mka_participant * +ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay) +{ + struct ieee802_1x_mka_participant *participant; + + dl_list_for_each(participant, &kay->participant_list, + struct ieee802_1x_mka_participant, list) { + if (participant->principal) + return participant; + } + + wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded"); + return NULL; +} + + +static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers, + const u8 *mi) +{ + struct ieee802_1x_kay_peer *peer; + + dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) { + if (os_memcmp(peer->mi, mi, MI_LEN) == 0) + return peer; + } + + return NULL; +} + + +/** + * ieee802_1x_kay_is_in_potential_peer + */ +static Boolean +ieee802_1x_kay_is_in_potential_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi) +{ + return get_peer_mi(&participant->potential_peers, mi) != NULL; +} + + +/** + * ieee802_1x_kay_is_in_live_peer + */ +static Boolean +ieee802_1x_kay_is_in_live_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi) +{ + return get_peer_mi(&participant->live_peers, mi) != NULL; +} + + +/** + * ieee802_1x_kay_is_in_peer + */ +static Boolean +ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi) +{ + return ieee802_1x_kay_is_in_live_peer(participant, mi) || + ieee802_1x_kay_is_in_potential_peer(participant, mi); +} + + +/** + * ieee802_1x_kay_get_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi) +{ + struct ieee802_1x_kay_peer *peer; + + peer = get_peer_mi(&participant->live_peers, mi); + if (peer) + return peer; + + return get_peer_mi(&participant->potential_peers, mi); +} + + +/** + * ieee802_1x_kay_get_live_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi) +{ + return get_peer_mi(&participant->live_peers, mi); +} + + +/** + * ieee802_1x_kay_get_cipher_suite + */ +static struct macsec_ciphersuite * +ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, + u8 *cs_id) +{ + unsigned int i; + + for (i = 0; i < CS_TABLE_SIZE; i++) { + if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0) + break; + } + if (i >= CS_TABLE_SIZE) + return NULL; + + return &cipher_suite_tbl[i]; +} + + +/** + * ieee802_1x_kay_get_peer_sci + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant, + const struct ieee802_1x_mka_sci *sci) +{ + struct ieee802_1x_kay_peer *peer; + + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0) + return peer; + } + + dl_list_for_each(peer, &participant->potential_peers, + struct ieee802_1x_kay_peer, list) { + if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0) + return peer; + } + + return NULL; +} + + +/** + * ieee802_1x_kay_init_receive_sa - + */ +static struct receive_sa * +ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, + struct data_key *key) +{ + struct receive_sa *psa; + + if (!psc || !key) + return NULL; + + psa = os_zalloc(sizeof(*psa)); + if (!psa) { + wpa_printf(MSG_ERROR, "%s: out of memory", __func__); + return NULL; + } + + psa->pkey = key; + psa->lowest_pn = lowest_pn; + psa->next_pn = lowest_pn; + psa->an = an; + psa->sc = psc; + + os_get_time(&psa->created_time); + psa->in_use = FALSE; + + dl_list_add(&psc->sa_list, &psa->list); + wpa_printf(MSG_DEBUG, + "KaY: Create receive SA(AN: %d lowest_pn: %u of SC(channel: %d)", + (int) an, lowest_pn, psc->channel); + + return psa; +} + + +/** + * ieee802_1x_kay_deinit_receive_sa - + */ +static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) +{ + psa->pkey = NULL; + wpa_printf(MSG_DEBUG, + "KaY: Delete receive SA(an: %d) of SC(channel: %d)", + psa->an, psa->sc->channel); + dl_list_del(&psa->list); + os_free(psa); +} + + +/** + * ieee802_1x_kay_init_receive_sc - + */ +static struct receive_sc * +ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci, + int channel) +{ + struct receive_sc *psc; + + if (!psci) + return NULL; + + psc = os_zalloc(sizeof(*psc)); + if (!psc) { + wpa_printf(MSG_ERROR, "%s: out of memory", __func__); + return NULL; + } + + os_memcpy(&psc->sci, psci, sizeof(psc->sci)); + psc->channel = channel; + + os_get_time(&psc->created_time); + psc->receiving = FALSE; + + dl_list_init(&psc->sa_list); + wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel); + wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci)); + + return psc; +} + + +/** + * ieee802_1x_kay_deinit_receive_sc - + **/ +static void +ieee802_1x_kay_deinit_receive_sc( + struct ieee802_1x_mka_participant *participant, struct receive_sc *psc) +{ + struct receive_sa *psa, *pre_sa; + + wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)", + psc->channel); + dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa, + list) { + secy_disable_receive_sa(participant->kay, psa); + ieee802_1x_kay_deinit_receive_sa(psa); + } + dl_list_del(&psc->list); + os_free(psc); +} + + +/** + * ieee802_1x_kay_create_live_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, + u8 *mi, u32 mn) +{ + struct ieee802_1x_kay_peer *peer; + struct receive_sc *rxsc; + u32 sc_ch = 0; + + peer = os_zalloc(sizeof(*peer)); + if (peer == NULL) { + wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + return NULL; + } + + os_memcpy(peer->mi, mi, MI_LEN); + peer->mn = mn; + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + peer->sak_used = FALSE; + os_memcpy(&peer->sci, &participant->current_peer_sci, + sizeof(peer->sci)); + dl_list_add(&participant->live_peers, &peer->list); + + secy_get_available_receive_sc(participant->kay, &sc_ch); + + rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); + if (!rxsc) + return NULL; + + dl_list_add(&participant->rxsc_list, &rxsc->list); + secy_create_receive_sc(participant->kay, rxsc); + + wpa_printf(MSG_DEBUG, "KaY: Live peer created"); + wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); + wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); + wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + + return peer; +} + + +/** + * ieee802_1x_kay_create_potential_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_create_potential_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn) +{ + struct ieee802_1x_kay_peer *peer; + + peer = os_zalloc(sizeof(*peer)); + if (peer == NULL) { + wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + return NULL; + } + + os_memcpy(peer->mi, mi, MI_LEN); + peer->mn = mn; + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + peer->sak_used = FALSE; + + dl_list_add(&participant->potential_peers, &peer->list); + + wpa_printf(MSG_DEBUG, "KaY: potential peer created"); + wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); + wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); + wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + + return peer; +} + + +/** + * ieee802_1x_kay_move_live_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, + u8 *mi, u32 mn) +{ + struct ieee802_1x_kay_peer *peer; + struct receive_sc *rxsc; + u32 sc_ch = 0; + + dl_list_for_each(peer, &participant->potential_peers, + struct ieee802_1x_kay_peer, list) { + if (os_memcmp(peer->mi, mi, MI_LEN) == 0) + break; + } + + os_memcpy(&peer->sci, &participant->current_peer_sci, + sizeof(peer->sci)); + peer->mn = mn; + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + + wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer"); + wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi)); + wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); + wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port); + + dl_list_del(&peer->list); + dl_list_add_tail(&participant->live_peers, &peer->list); + + secy_get_available_receive_sc(participant->kay, &sc_ch); + + rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); + if (!rxsc) + return NULL; + + dl_list_add(&participant->rxsc_list, &rxsc->list); + secy_create_receive_sc(participant->kay, rxsc); + + return peer; +} + + + +/** + * ieee802_1x_mka_basic_body_present - + */ +static Boolean +ieee802_1x_mka_basic_body_present( + struct ieee802_1x_mka_participant *participant) +{ + return TRUE; +} + + +/** + * ieee802_1x_mka_basic_body_length - + */ +static int +ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant) +{ + int length; + + length = sizeof(struct ieee802_1x_mka_basic_body); + length += participant->ckn.len; + return (length + 0x3) & ~0x3; +} + + +/** + * ieee802_1x_mka_encode_basic_body + */ +static int +ieee802_1x_mka_encode_basic_body( + struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf) +{ + struct ieee802_1x_mka_basic_body *body; + struct ieee802_1x_kay *kay = participant->kay; + unsigned int length = ieee802_1x_mka_basic_body_length(participant); + + body = wpabuf_put(buf, length); + + body->version = kay->mka_version; + body->priority = kay->actor_priority; + if (participant->is_elected) + body->key_server = participant->is_key_server; + else + body->key_server = participant->can_be_key_server; + + body->macsec_desired = kay->macsec_desired; + body->macsec_capbility = kay->macsec_capable; + set_mka_param_body_len(body, length - MKA_HDR_LEN); + + os_memcpy(body->actor_sci.addr, kay->actor_sci.addr, + sizeof(kay->actor_sci.addr)); + body->actor_sci.port = host_to_be16(kay->actor_sci.port); + + os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi)); + participant->mn = participant->mn + 1; + body->actor_mn = host_to_be32(participant->mn); + os_memcpy(body->algo_agility, participant->kay->algo_agility, + sizeof(body->algo_agility)); + + os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len); + + ieee802_1x_mka_dump_basic_body(body); + + return 0; +} + + +/** + * ieee802_1x_mka_decode_basic_body - + */ +static struct ieee802_1x_mka_participant * +ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, + size_t msg_len) +{ + struct ieee802_1x_mka_participant *participant; + const struct ieee802_1x_mka_basic_body *body; + struct ieee802_1x_kay_peer *peer; + + body = (const struct ieee802_1x_mka_basic_body *) mka_msg; + + if (body->version > MKA_VERSION_ID) { + wpa_printf(MSG_DEBUG, + "KaY: peer's version(%d) greater than mka current version(%d)", + body->version, MKA_VERSION_ID); + } + if (kay->is_obliged_key_server && body->key_server) { + wpa_printf(MSG_DEBUG, "I must be as key server"); + return NULL; + } + + participant = ieee802_1x_kay_get_participant(kay, body->ckn); + if (!participant) { + wpa_printf(MSG_DEBUG, "Peer is not included in my CA"); + return NULL; + } + + /* If the peer's MI is my MI, I will choose new MI */ + if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { + if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + return NULL; + participant->mn = 0; + } + + os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN); + participant->current_peer_id.mn = be_to_host32(body->actor_mn); + os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr, + sizeof(participant->current_peer_sci.addr)); + participant->current_peer_sci.port = be_to_host16(body->actor_sci.port); + + /* handler peer */ + peer = ieee802_1x_kay_get_peer(participant, body->actor_mi); + if (!peer) { + /* Check duplicated SCI */ + /* TODO: What policy should be applied to detect duplicated SCI + * is active attacker or a valid peer whose MI is be changed? + */ + peer = ieee802_1x_kay_get_peer_sci(participant, + &body->actor_sci); + if (peer) { + wpa_printf(MSG_WARNING, + "KaY: duplicated SCI detected, Maybe active attacker"); + dl_list_del(&peer->list); + os_free(peer); + } + + peer = ieee802_1x_kay_create_potential_peer( + participant, body->actor_mi, + be_to_host32(body->actor_mn)); + if (!peer) + return NULL; + + peer->macsec_desired = body->macsec_desired; + peer->macsec_capbility = body->macsec_capbility; + peer->is_key_server = (Boolean) body->key_server; + peer->key_server_priority = body->priority; + } else if (peer->mn < be_to_host32(body->actor_mn)) { + peer->mn = be_to_host32(body->actor_mn); + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + peer->macsec_desired = body->macsec_desired; + peer->macsec_capbility = body->macsec_capbility; + peer->is_key_server = (Boolean) body->key_server; + peer->key_server_priority = body->priority; + } else { + wpa_printf(MSG_WARNING, "KaY: The peer MN have received"); + return NULL; + } + + return participant; +} + + +/** + * ieee802_1x_mka_live_peer_body_present + */ +static Boolean +ieee802_1x_mka_live_peer_body_present( + struct ieee802_1x_mka_participant *participant) +{ + return !dl_list_empty(&participant->live_peers); +} + + +/** + * ieee802_1x_kay_get_live_peer_length + */ +static int +ieee802_1x_mka_get_live_peer_length( + struct ieee802_1x_mka_participant *participant) +{ + int len = MKA_HDR_LEN; + struct ieee802_1x_kay_peer *peer; + + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) + len += sizeof(struct ieee802_1x_mka_peer_id); + + return (len + 0x3) & ~0x3; +} + + +/** + * ieee802_1x_mka_encode_live_peer_body - + */ +static int +ieee802_1x_mka_encode_live_peer_body( + struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf) +{ + struct ieee802_1x_mka_peer_body *body; + struct ieee802_1x_kay_peer *peer; + unsigned int length; + struct ieee802_1x_mka_peer_id *body_peer; + + length = ieee802_1x_mka_get_live_peer_length(participant); + body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body)); + + body->type = MKA_LIVE_PEER_LIST; + set_mka_param_body_len(body, length - MKA_HDR_LEN); + + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + body_peer = wpabuf_put(buf, + sizeof(struct ieee802_1x_mka_peer_id)); + os_memcpy(body_peer->mi, peer->mi, MI_LEN); + body_peer->mn = host_to_be32(peer->mn); + body_peer++; + } + + ieee802_1x_mka_dump_peer_body(body); + return 0; +} + +/** + * ieee802_1x_mka_potential_peer_body_present + */ +static Boolean +ieee802_1x_mka_potential_peer_body_present( + struct ieee802_1x_mka_participant *participant) +{ + return !dl_list_empty(&participant->potential_peers); +} + + +/** + * ieee802_1x_kay_get_potential_peer_length + */ +static int +ieee802_1x_mka_get_potential_peer_length( + struct ieee802_1x_mka_participant *participant) +{ + int len = MKA_HDR_LEN; + struct ieee802_1x_kay_peer *peer; + + dl_list_for_each(peer, &participant->potential_peers, + struct ieee802_1x_kay_peer, list) + len += sizeof(struct ieee802_1x_mka_peer_id); + + return (len + 0x3) & ~0x3; +} + + +/** + * ieee802_1x_mka_encode_potential_peer_body - + */ +static int +ieee802_1x_mka_encode_potential_peer_body( + struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf) +{ + struct ieee802_1x_mka_peer_body *body; + struct ieee802_1x_kay_peer *peer; + unsigned int length; + struct ieee802_1x_mka_peer_id *body_peer; + + length = ieee802_1x_mka_get_potential_peer_length(participant); + body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body)); + + body->type = MKA_POTENTIAL_PEER_LIST; + set_mka_param_body_len(body, length - MKA_HDR_LEN); + + dl_list_for_each(peer, &participant->potential_peers, + struct ieee802_1x_kay_peer, list) { + body_peer = wpabuf_put(buf, + sizeof(struct ieee802_1x_mka_peer_id)); + os_memcpy(body_peer->mi, peer->mi, MI_LEN); + body_peer->mn = host_to_be32(peer->mn); + body_peer++; + } + + ieee802_1x_mka_dump_peer_body(body); + return 0; +} + + +/** + * ieee802_1x_mka_i_in_peerlist - + */ +static Boolean +ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + Boolean included = FALSE; + struct ieee802_1x_mka_hdr *hdr; + size_t body_len; + size_t left_len; + int body_type; + u32 peer_mn; + const u8 *peer_mi; + const u8 *pos; + size_t i; + + pos = mka_msg; + left_len = msg_len; + while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) { + hdr = (struct ieee802_1x_mka_hdr *) pos; + body_len = get_mka_param_body_len(hdr); + body_type = get_mka_param_body_type(hdr); + + if (body_type != MKA_LIVE_PEER_LIST && + body_type != MKA_POTENTIAL_PEER_LIST) + goto SKIP_PEER; + + ieee802_1x_mka_dump_peer_body( + (struct ieee802_1x_mka_peer_body *)pos); + + if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV", + (int) left_len, (int) MKA_HDR_LEN, + (int) body_len, DEFAULT_ICV_LEN); + goto SKIP_PEER; + } + + if ((body_len % 16) != 0) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets", + (int) body_len); + goto SKIP_PEER; + } + + for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { + peer_mi = MKA_HDR_LEN + pos + i; + os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); + peer_mn = be_to_host32(peer_mn); + if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 && + peer_mn == participant->mn) { + included = TRUE; + break; + } + } + + if (included) + return TRUE; + +SKIP_PEER: + left_len -= body_len + MKA_HDR_LEN; + pos += body_len + MKA_HDR_LEN; + } + + return FALSE; +} + + +/** + * ieee802_1x_mka_decode_live_peer_body - + */ +static int ieee802_1x_mka_decode_live_peer_body( + struct ieee802_1x_mka_participant *participant, + const u8 *peer_msg, size_t msg_len) +{ + const struct ieee802_1x_mka_hdr *hdr; + struct ieee802_1x_kay_peer *peer; + size_t body_len; + u32 peer_mn; + const u8 *peer_mi; + size_t i; + Boolean is_included; + + is_included = ieee802_1x_kay_is_in_live_peer( + participant, participant->current_peer_id.mi); + + hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; + body_len = get_mka_param_body_len(hdr); + + for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { + peer_mi = MKA_HDR_LEN + peer_msg + i; + os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); + peer_mn = be_to_host32(peer_mn); + + /* it is myself */ + if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { + /* My message id is used by other participant */ + if (peer_mn > participant->mn) { + if (os_get_random(participant->mi, + sizeof(participant->mi)) < 0) + wpa_printf(MSG_DEBUG, + "KaY: Could not update mi"); + participant->mn = 0; + } + continue; + } + if (!is_included) + continue; + + peer = ieee802_1x_kay_get_peer(participant, peer_mi); + if (NULL != peer) { + peer->mn = peer_mn; + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + } else { + if (!ieee802_1x_kay_create_potential_peer( + participant, peer_mi, peer_mn)) { + return -1; + } + } + } + + return 0; +} + + +/** + * ieee802_1x_mka_decode_potential_peer_body - + */ +static int +ieee802_1x_mka_decode_potential_peer_body( + struct ieee802_1x_mka_participant *participant, + const u8 *peer_msg, size_t msg_len) +{ + struct ieee802_1x_mka_hdr *hdr; + size_t body_len; + u32 peer_mn; + const u8 *peer_mi; + size_t i; + + hdr = (struct ieee802_1x_mka_hdr *) peer_msg; + body_len = get_mka_param_body_len(hdr); + + for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { + peer_mi = MKA_HDR_LEN + peer_msg + i; + os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); + peer_mn = be_to_host32(peer_mn); + + /* it is myself */ + if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { + /* My message id is used by other participant */ + if (peer_mn > participant->mn) { + if (os_get_random(participant->mi, + sizeof(participant->mi)) < 0) + wpa_printf(MSG_DEBUG, + "KaY: Could not update mi"); + participant->mn = 0; + } + continue; + } + } + + return 0; +} + + +/** + * ieee802_1x_mka_sak_use_body_present + */ +static Boolean +ieee802_1x_mka_sak_use_body_present( + struct ieee802_1x_mka_participant *participant) +{ + if (participant->to_use_sak) + return TRUE; + else + return FALSE; +} + + +/** + * ieee802_1x_mka_get_sak_use_length + */ +static int +ieee802_1x_mka_get_sak_use_length( + struct ieee802_1x_mka_participant *participant) +{ + int length = MKA_HDR_LEN; + + if (participant->kay->macsec_desired && participant->advised_desired) + length = sizeof(struct ieee802_1x_mka_sak_use_body); + else + length = MKA_HDR_LEN; + + length = (length + 0x3) & ~0x3; + + return length; +} + + +/** + * + */ +static u32 +ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal, + struct ieee802_1x_mka_ki *ki) +{ + struct receive_sa *rxsa; + struct receive_sc *rxsc; + u32 lpn = 0; + + dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { + dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) + { + if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) { + secy_get_receive_lowest_pn(principal->kay, + rxsa); + + lpn = lpn > rxsa->lowest_pn ? + lpn : rxsa->lowest_pn; + break; + } + } + } + + if (lpn == 0) + lpn = 1; + + return lpn; +} + + +/** + * ieee802_1x_mka_encode_sak_use_body - + */ +static int +ieee802_1x_mka_encode_sak_use_body( + struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf) +{ + struct ieee802_1x_mka_sak_use_body *body; + unsigned int length; + u32 pn = 1; + + length = ieee802_1x_mka_get_sak_use_length(participant); + body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body)); + + body->type = MKA_SAK_USE; + set_mka_param_body_len(body, length - MKA_HDR_LEN); + + if (length == MKA_HDR_LEN) { + body->ptx = TRUE; + body->prx = TRUE; + body->lan = 0; + body->lrx = FALSE; + body->ltx = FALSE; + body->delay_protect = FALSE; + return 0; + } + + /* data protect, lowest accept packet number */ + body->delay_protect = participant->kay->macsec_replay_protect; + pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); + if (pn > participant->kay->pn_exhaustion) { + wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); + if (participant->is_key_server) + participant->new_sak = TRUE; + } + + body->llpn = host_to_be32(pn); + pn = ieee802_1x_mka_get_lpn(participant, &participant->oki); + body->olpn = host_to_be32(pn); + + /* plain tx, plain rx */ + if (participant->kay->macsec_protect) + body->ptx = FALSE; + else + body->ptx = TRUE; + + if (participant->kay->macsec_validate == Strict) + body->prx = FALSE; + else + body->prx = TRUE; + + /* latest key: rx, tx, key server member identifier key number */ + body->lan = participant->lan; + os_memcpy(body->lsrv_mi, participant->lki.mi, + sizeof(body->lsrv_mi)); + body->lkn = host_to_be32(participant->lki.kn); + body->lrx = participant->lrx; + body->ltx = participant->ltx; + + /* old key: rx, tx, key server member identifier key number */ + body->oan = participant->oan; + if (participant->oki.kn != participant->lki.kn && + participant->oki.kn != 0) { + body->otx = TRUE; + body->orx = TRUE; + os_memcpy(body->osrv_mi, participant->oki.mi, + sizeof(body->osrv_mi)); + body->okn = host_to_be32(participant->oki.kn); + } else { + body->otx = FALSE; + body->orx = FALSE; + } + + /* set CP's variable */ + if (body->ltx) { + if (!participant->kay->tx_enable) + participant->kay->tx_enable = TRUE; + + if (!participant->kay->port_enable) + participant->kay->port_enable = TRUE; + } + if (body->lrx) { + if (!participant->kay->rx_enable) + participant->kay->rx_enable = TRUE; + } + + ieee802_1x_mka_dump_sak_use_body(body); + return 0; +} + + +/** + * ieee802_1x_mka_decode_sak_use_body - + */ +static int +ieee802_1x_mka_decode_sak_use_body( + struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + struct ieee802_1x_mka_hdr *hdr; + struct ieee802_1x_mka_sak_use_body *body; + struct ieee802_1x_kay_peer *peer; + struct transmit_sa *txsa; + struct data_key *sa_key = NULL; + size_t body_len; + struct ieee802_1x_mka_ki ki; + u32 lpn; + Boolean all_receiving; + Boolean founded; + + if (!participant->principal) { + wpa_printf(MSG_WARNING, "KaY: Participant is not principal"); + return -1; + } + peer = ieee802_1x_kay_get_live_peer(participant, + participant->current_peer_id.mi); + if (!peer) { + wpa_printf(MSG_WARNING, "KaY: the peer is not my live peer"); + return -1; + } + + hdr = (struct ieee802_1x_mka_hdr *) mka_msg; + body_len = get_mka_param_body_len(hdr); + body = (struct ieee802_1x_mka_sak_use_body *) mka_msg; + ieee802_1x_mka_dump_sak_use_body(body); + + if ((body_len != 0) && (body_len < 40)) { + wpa_printf(MSG_ERROR, + "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets", + (int) body_len); + return -1; + } + + /* TODO: what action should I take when peer does not support MACsec */ + if (body_len == 0) { + wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec"); + return 0; + } + + /* TODO: when the plain tx or rx of peer is true, should I change + * the attribute of controlled port + */ + if (body->prx) + wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE"); + + if (body->ptx) + wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE"); + + /* check latest key is valid */ + if (body->ltx || body->lrx) { + founded = FALSE; + os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); + ki.kn = ntohl(body->lkn); + dl_list_for_each(sa_key, &participant->sak_list, + struct data_key, list) { + if (is_ki_equal(&sa_key->key_identifier, &ki)) { + founded = TRUE; + break; + } + } + if (!founded) { + wpa_printf(MSG_WARNING, "KaY: Latest key is invalid"); + return -1; + } + if (os_memcmp(participant->lki.mi, body->lsrv_mi, + sizeof(participant->lki.mi)) == 0 && + ntohl(body->lkn) == participant->lki.kn && + body->lan == participant->lan) { + peer->sak_used = TRUE; + } + if (body->ltx && peer->is_key_server) { + ieee802_1x_cp_set_servertransmitting( + participant->kay->cp, TRUE); + ieee802_1x_cp_sm_step(participant->kay->cp); + } + } + + /* check old key is valid */ + if (body->otx || body->orx) { + if (os_memcmp(participant->oki.mi, body->osrv_mi, + sizeof(participant->oki.mi)) != 0 || + ntohl(body->okn) != participant->oki.kn || + body->oan != participant->oan) { + wpa_printf(MSG_WARNING, "KaY: Old key is invalid"); + return -1; + } + } + + /* TODO: how to set the MACsec hardware when delay_protect is true */ + if (body->delay_protect && (!ntohl(body->llpn) || !ntohl(body->olpn))) { + wpa_printf(MSG_WARNING, + "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE"); + return -1; + } + + /* check all live peer have used the sak for receiving sa */ + all_receiving = TRUE; + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + if (!peer->sak_used) { + all_receiving = FALSE; + break; + } + } + if (all_receiving) { + participant->to_dist_sak = FALSE; + ieee802_1x_cp_set_allreceiving(participant->kay->cp, TRUE); + ieee802_1x_cp_sm_step(participant->kay->cp); + } + + /* if i'm key server, and detects peer member pn exhaustion, rekey.*/ + lpn = ntohl(body->llpn); + if (lpn > participant->kay->pn_exhaustion) { + if (participant->is_key_server) { + participant->new_sak = TRUE; + wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); + } + } + + founded = FALSE; + dl_list_for_each(txsa, &participant->txsc->sa_list, + struct transmit_sa, list) { + if (sa_key != NULL && txsa->pkey == sa_key) { + founded = TRUE; + break; + } + } + if (!founded) { + wpa_printf(MSG_WARNING, "KaY: Can't find txsa"); + return -1; + } + + /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key + * npn is larger than txsa's npn, set it to txsa. + */ + secy_get_transmit_next_pn(participant->kay, txsa); + if (lpn > txsa->next_pn) { + secy_set_transmit_next_pn(participant->kay, txsa); + wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn); + } + + return 0; +} + + +/** + * ieee802_1x_mka_dist_sak_body_present + */ +static Boolean +ieee802_1x_mka_dist_sak_body_present( + struct ieee802_1x_mka_participant *participant) +{ + if (!participant->to_dist_sak || !participant->new_key) + return FALSE; + + return TRUE; +} + + +/** + * ieee802_1x_kay_get_dist_sak_length + */ +static int +ieee802_1x_mka_get_dist_sak_length( + struct ieee802_1x_mka_participant *participant) +{ + int length; + int cs_index = participant->kay->macsec_csindex; + + if (participant->advised_desired) { + length = sizeof(struct ieee802_1x_mka_dist_sak_body); + if (cs_index != DEFAULT_CS_INDEX) + length += CS_ID_LEN; + + length += cipher_suite_tbl[cs_index].sak_len + 8; + } else { + length = MKA_HDR_LEN; + } + length = (length + 0x3) & ~0x3; + + return length; +} + + +/** + * ieee802_1x_mka_encode_dist_sak_body - + */ +static int +ieee802_1x_mka_encode_dist_sak_body( + struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf) +{ + struct ieee802_1x_mka_dist_sak_body *body; + struct data_key *sak; + unsigned int length; + int cs_index; + int sak_pos; + + length = ieee802_1x_mka_get_dist_sak_length(participant); + body = wpabuf_put(buf, length); + body->type = MKA_DISTRIBUTED_SAK; + set_mka_param_body_len(body, length - MKA_HDR_LEN); + if (length == MKA_HDR_LEN) { + body->confid_offset = 0; + body->dan = 0; + return 0; + } + + sak = participant->new_key; + body->confid_offset = sak->confidentiality_offset; + body->dan = sak->an; + body->kn = host_to_be32(sak->key_identifier.kn); + cs_index = participant->kay->macsec_csindex; + sak_pos = 0; + if (cs_index != DEFAULT_CS_INDEX) { + os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN); + sak_pos = CS_ID_LEN; + } + if (aes_wrap(participant->kek.key, 16, + cipher_suite_tbl[cs_index].sak_len / 8, + sak->key, body->sak + sak_pos)) { + wpa_printf(MSG_ERROR, "KaY: AES wrap failed"); + return -1; + } + + ieee802_1x_mka_dump_dist_sak_body(body); + + return 0; +} + + +/** + * ieee802_1x_kay_init_data_key - + */ +static struct data_key * +ieee802_1x_kay_init_data_key(const struct key_conf *conf) +{ + struct data_key *pkey; + + if (!conf) + return NULL; + + pkey = os_zalloc(sizeof(*pkey)); + if (pkey == NULL) { + wpa_printf(MSG_ERROR, "%s: out of memory", __func__); + return NULL; + } + + pkey->key = os_zalloc(conf->key_len); + if (pkey->key == NULL) { + wpa_printf(MSG_ERROR, "%s: out of memory", __func__); + os_free(pkey); + return NULL; + } + + os_memcpy(pkey->key, conf->key, conf->key_len); + os_memcpy(&pkey->key_identifier, &conf->ki, + sizeof(pkey->key_identifier)); + pkey->confidentiality_offset = conf->offset; + pkey->an = conf->an; + pkey->transmits = conf->tx; + pkey->receives = conf->rx; + os_get_time(&pkey->created_time); + + pkey->user = 1; + + return pkey; +} + + +/** + * ieee802_1x_kay_decode_dist_sak_body - + */ +static int +ieee802_1x_mka_decode_dist_sak_body( + struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + struct ieee802_1x_mka_hdr *hdr; + struct ieee802_1x_mka_dist_sak_body *body; + struct ieee802_1x_kay_peer *peer; + struct macsec_ciphersuite *cs; + size_t body_len; + struct key_conf *conf; + struct data_key *sa_key = NULL; + struct ieee802_1x_mka_ki sak_ki; + int sak_len; + u8 *wrap_sak; + u8 *unwrap_sak; + + hdr = (struct ieee802_1x_mka_hdr *) mka_msg; + body_len = get_mka_param_body_len(hdr); + if ((body_len != 0) && (body_len != 28) && (body_len < 36)) { + wpa_printf(MSG_ERROR, + "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets", + (int) body_len); + return -1; + } + + if (!participant->principal) { + wpa_printf(MSG_ERROR, + "KaY: I can't accept the distributed SAK as I am not principal"); + return -1; + } + if (participant->is_key_server) { + wpa_printf(MSG_ERROR, + "KaY: I can't accept the distributed SAK as myself is key server "); + return -1; + } + if (!participant->kay->macsec_desired || + participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { + wpa_printf(MSG_ERROR, + "KaY: I am not MACsec-desired or without MACsec capable"); + return -1; + } + + peer = ieee802_1x_kay_get_live_peer(participant, + participant->current_peer_id.mi); + if (!peer) { + wpa_printf(MSG_ERROR, + "KaY: The key server is not in my live peers list"); + return -1; + } + if (os_memcmp(&participant->kay->key_server_sci, + &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) { + wpa_printf(MSG_ERROR, "KaY: The key server is not elected"); + return -1; + } + if (body_len == 0) { + participant->kay->authenticated = TRUE; + participant->kay->secured = FALSE; + participant->kay->failed = FALSE; + participant->advised_desired = FALSE; + ieee802_1x_cp_connect_authenticated(participant->kay->cp); + ieee802_1x_cp_sm_step(participant->kay->cp); + wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); + participant->to_use_sak = TRUE; + return 0; + } + participant->advised_desired = TRUE; + participant->kay->authenticated = FALSE; + participant->kay->secured = TRUE; + participant->kay->failed = FALSE; + ieee802_1x_cp_connect_secure(participant->kay->cp); + ieee802_1x_cp_sm_step(participant->kay->cp); + + body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg; + ieee802_1x_mka_dump_dist_sak_body(body); + dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list) + { + if (os_memcmp(sa_key->key_identifier.mi, + participant->current_peer_id.mi, MI_LEN) == 0 && + sa_key->key_identifier.kn == be_to_host32(body->kn)) { + wpa_printf(MSG_WARNING, "KaY:The Key has installed"); + return 0; + } + } + if (body_len == 28) { + sak_len = DEFAULT_SA_KEY_LEN; + wrap_sak = body->sak; + participant->kay->macsec_csindex = DEFAULT_CS_INDEX; + } else { + cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak); + if (!cs) { + wpa_printf(MSG_ERROR, + "KaY: I can't support the Cipher Suite advised by key server"); + return -1; + } + sak_len = cs->sak_len; + wrap_sak = body->sak + CS_ID_LEN; + participant->kay->macsec_csindex = cs->index; + } + + unwrap_sak = os_zalloc(sak_len); + if (!unwrap_sak) { + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + return -1; + } + if (aes_unwrap(participant->kek.key, 16, sak_len >> 3, wrap_sak, + unwrap_sak)) { + wpa_printf(MSG_ERROR, "KaY: AES unwrap failed"); + os_free(unwrap_sak); + return -1; + } + wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len); + + conf = os_zalloc(sizeof(*conf)); + if (!conf) { + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + os_free(unwrap_sak); + return -1; + } + conf->key_len = sak_len; + + conf->key = os_zalloc(conf->key_len); + if (!conf->key) { + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + os_free(unwrap_sak); + os_free(conf); + return -1; + } + + os_memcpy(conf->key, unwrap_sak, conf->key_len); + + os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi, + sizeof(sak_ki.mi)); + sak_ki.kn = be_to_host32(body->kn); + + os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN); + conf->ki.kn = sak_ki.kn; + conf->an = body->dan; + conf->offset = body->confid_offset; + conf->rx = TRUE; + conf->tx = TRUE; + + sa_key = ieee802_1x_kay_init_data_key(conf); + if (!sa_key) { + os_free(unwrap_sak); + os_free(conf->key); + os_free(conf); + return -1; + } + + dl_list_add(&participant->sak_list, &sa_key->list); + + ieee802_1x_cp_set_ciphersuite( + participant->kay->cp, + cipher_suite_tbl[participant->kay->macsec_csindex].id); + ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset); + ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki); + ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan); + ieee802_1x_cp_signal_newsak(participant->kay->cp); + ieee802_1x_cp_sm_step(participant->kay->cp); + + participant->to_use_sak = TRUE; + + os_free(unwrap_sak); + os_free(conf->key); + os_free(conf); + + return 0; +} + + +/** + * ieee802_1x_mka_icv_body_present + */ +static Boolean +ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant) +{ + return TRUE; +} + + +/** + * ieee802_1x_kay_get_icv_length + */ +static int +ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) +{ + int length; + + length = sizeof(struct ieee802_1x_mka_icv_body); + length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; + + return (length + 0x3) & ~0x3; +} + + +/** + * ieee802_1x_mka_encode_icv_body - + */ +static int +ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf) +{ + struct ieee802_1x_mka_icv_body *body; + unsigned int length; + u8 cmac[MAX_ICV_LEN]; + + length = ieee802_1x_mka_get_icv_length(participant); + if (length != DEFAULT_ICV_LEN) { + body = wpabuf_put(buf, MKA_HDR_LEN); + body->type = MKA_ICV_INDICATOR; + set_mka_param_body_len(body, length - MKA_HDR_LEN); + } + + if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash( + participant->ick.key, wpabuf_head(buf), buf->used, cmac)) { + wpa_printf(MSG_ERROR, "KaY, omac1_aes_128 failed"); + return -1; + } + + if (length != DEFAULT_ICV_LEN) { + os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac, + length - MKA_HDR_LEN); + } else { + os_memcpy(wpabuf_put(buf, length), cmac, length); + } + + return 0; +} + +/** + * ieee802_1x_mka_decode_icv_body - + */ +static u8 * +ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + struct ieee802_1x_mka_hdr *hdr; + struct ieee802_1x_mka_icv_body *body; + size_t body_len; + size_t left_len; + int body_type; + const u8 *pos; + + pos = mka_msg; + left_len = msg_len; + while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) { + hdr = (struct ieee802_1x_mka_hdr *) pos; + body_len = get_mka_param_body_len(hdr); + body_type = get_mka_param_body_type(hdr); + + if (left_len < (body_len + MKA_HDR_LEN)) + break; + + if (body_type != MKA_ICV_INDICATOR) { + left_len -= MKA_HDR_LEN + body_len; + pos += MKA_HDR_LEN + body_len; + continue; + } + + body = (struct ieee802_1x_mka_icv_body *)pos; + if (body_len + < mka_alg_tbl[participant->kay->mka_algindex].icv_len) { + return NULL; + } + + return body->icv; + } + + return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN); +} + + +/** + * ieee802_1x_mka_decode_dist_cak_body- + */ +static int +ieee802_1x_mka_decode_dist_cak_body( + struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + struct ieee802_1x_mka_hdr *hdr; + size_t body_len; + + hdr = (struct ieee802_1x_mka_hdr *) mka_msg; + body_len = get_mka_param_body_len(hdr); + if (body_len < 28) { + wpa_printf(MSG_ERROR, + "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 28 or more octets", + (int) body_len); + return -1; + } + + return 0; +} + + +/** + * ieee802_1x_mka_decode_kmd_body - + */ +static int +ieee802_1x_mka_decode_kmd_body( + struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + struct ieee802_1x_mka_hdr *hdr; + size_t body_len; + + hdr = (struct ieee802_1x_mka_hdr *) mka_msg; + body_len = get_mka_param_body_len(hdr); + if (body_len < 5) { + wpa_printf(MSG_ERROR, + "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 5 or more octets", + (int) body_len); + return -1; + } + + return 0; +} + + +/** + * ieee802_1x_mka_decode_announce_body - + */ +static int ieee802_1x_mka_decode_announce_body( + struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len) +{ + return 0; +} + + +static struct mka_param_body_handler mak_body_handler[] = { + /* basic parameter set */ + { + ieee802_1x_mka_encode_basic_body, + NULL, + ieee802_1x_mka_basic_body_length, + ieee802_1x_mka_basic_body_present + }, + + /* live peer list parameter set */ + { + ieee802_1x_mka_encode_live_peer_body, + ieee802_1x_mka_decode_live_peer_body, + ieee802_1x_mka_get_live_peer_length, + ieee802_1x_mka_live_peer_body_present + }, + + /* potential peer list parameter set */ + { + ieee802_1x_mka_encode_potential_peer_body, + ieee802_1x_mka_decode_potential_peer_body, + ieee802_1x_mka_get_potential_peer_length, + ieee802_1x_mka_potential_peer_body_present + }, + + /* sak use parameter set */ + { + ieee802_1x_mka_encode_sak_use_body, + ieee802_1x_mka_decode_sak_use_body, + ieee802_1x_mka_get_sak_use_length, + ieee802_1x_mka_sak_use_body_present + }, + + /* distribute sak parameter set */ + { + ieee802_1x_mka_encode_dist_sak_body, + ieee802_1x_mka_decode_dist_sak_body, + ieee802_1x_mka_get_dist_sak_length, + ieee802_1x_mka_dist_sak_body_present + }, + + /* distribute cak parameter set */ + { + NULL, + ieee802_1x_mka_decode_dist_cak_body, + NULL, + NULL + }, + + /* kmd parameter set */ + { + NULL, + ieee802_1x_mka_decode_kmd_body, + NULL, + NULL + }, + + /* announce parameter set */ + { + NULL, + ieee802_1x_mka_decode_announce_body, + NULL, + NULL + }, + + /* icv parameter set */ + { + ieee802_1x_mka_encode_icv_body, + NULL, + ieee802_1x_mka_get_icv_length, + ieee802_1x_mka_icv_body_present + }, +}; + + +/** + * ieee802_1x_kay_deinit_data_key - + */ +void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) +{ + if (!pkey) + return; + + pkey->user--; + if (pkey->user > 1) + return; + + dl_list_del(&pkey->list); + os_free(pkey->key); + os_free(pkey); +} + + +/** + * ieee802_1x_kay_generate_new_sak - + */ +static int +ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) +{ + struct data_key *sa_key = NULL; + struct key_conf *conf; + struct ieee802_1x_kay_peer *peer; + struct ieee802_1x_kay *kay = participant->kay; + int ctx_len, ctx_offset; + u8 *context; + + /* check condition for generating a fresh SAK: + * must have one live peer + * and MKA life time elapse since last distribution + * or potential peer is empty + */ + if (dl_list_empty(&participant->live_peers)) { + wpa_printf(MSG_ERROR, + "KaY: Live peers list must not empty when generating fresh SAK"); + return -1; + } + + /* FIXME: A fresh SAK not generated until + * the live peer list contains at least one peer and + * MKA life time has elapsed since the prior SAK was first distributed, + * or the Key server's potential peer is empty + * but I can't understand the second item, so + * here only check first item and ingore + * && (!dl_list_empty(&participant->potential_peers))) { + */ + if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) { + wpa_printf(MSG_ERROR, + "KaY: Life time have not elapsed since prior SAK distributed"); + return -1; + } + + conf = os_zalloc(sizeof(*conf)); + if (!conf) { + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + return -1; + } + conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len; + + conf->key = os_zalloc(conf->key_len); + if (!conf->key) { + os_free(conf); + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + return -1; + } + + ctx_len = conf->key_len + sizeof(kay->dist_kn); + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) + ctx_len += sizeof(peer->mi); + ctx_len += sizeof(participant->mi); + + context = os_zalloc(ctx_len); + if (!context) { + os_free(conf->key); + os_free(conf); + return -1; + } + ctx_offset = 0; + if (os_get_random(context + ctx_offset, conf->key_len) < 0) { + os_free(context); + os_free(conf->key); + os_free(conf); + return -1; + } + ctx_offset += conf->key_len; + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi)); + ctx_offset += sizeof(peer->mi); + } + os_memcpy(context + ctx_offset, participant->mi, + sizeof(participant->mi)); + ctx_offset += sizeof(participant->mi); + os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn)); + + if (conf->key_len == 16) { + ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, + context, ctx_len, conf->key); + } else if (conf->key_len == 32) { + ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, + context, ctx_len, conf->key); + } else { + wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); + os_free(conf->key); + os_free(conf); + os_free(context); + return -1; + } + wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", + conf->key, conf->key_len); + + os_memcpy(conf->ki.mi, participant->mi, MI_LEN); + conf->ki.kn = participant->kay->dist_kn; + conf->an = participant->kay->dist_an; + conf->offset = kay->macsec_confidentiality; + conf->rx = TRUE; + conf->tx = TRUE; + + sa_key = ieee802_1x_kay_init_data_key(conf); + if (!sa_key) { + os_free(conf->key); + os_free(conf); + os_free(context); + return -1; + } + participant->new_key = sa_key; + + dl_list_add(&participant->sak_list, &sa_key->list); + ieee802_1x_cp_set_ciphersuite(participant->kay->cp, + cipher_suite_tbl[kay->macsec_csindex].id); + ieee802_1x_cp_sm_step(kay->cp); + ieee802_1x_cp_set_offset(kay->cp, conf->offset); + ieee802_1x_cp_sm_step(kay->cp); + ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki); + ieee802_1x_cp_set_distributedan(kay->cp, conf->an); + ieee802_1x_cp_signal_newsak(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) + peer->sak_used = FALSE; + + participant->kay->dist_kn++; + participant->kay->dist_an++; + if (participant->kay->dist_an > 3) + participant->kay->dist_an = 0; + + participant->kay->dist_time = time(NULL); + + os_free(conf->key); + os_free(conf); + os_free(context); + return 0; +} + + +/** + * ieee802_1x_kay_elect_key_server - elect the key server + * when to elect: whenever the live peers list changes + */ +static int +ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) +{ + struct ieee802_1x_kay_peer *peer; + struct ieee802_1x_kay_peer *key_server = NULL; + struct ieee802_1x_kay *kay = participant->kay; + Boolean i_is_key_server; + int i; + + if (participant->is_obliged_key_server) { + participant->new_sak = TRUE; + participant->to_dist_sak = FALSE; + ieee802_1x_cp_set_electedself(kay->cp, TRUE); + return 0; + } + + /* elect the key server among the peers */ + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + if (!peer->is_key_server) + continue; + + if (!key_server) { + key_server = peer; + continue; + } + + if (peer->key_server_priority < + key_server->key_server_priority) { + key_server = peer; + } else if (peer->key_server_priority == + key_server->key_server_priority) { + for (i = 0; i < 6; i++) { + if (peer->sci.addr[i] < + key_server->sci.addr[i]) + key_server = peer; + } + } + } + + /* elect the key server between me and the above elected peer */ + i_is_key_server = FALSE; + if (key_server && participant->can_be_key_server) { + if (kay->actor_priority + < key_server->key_server_priority) { + i_is_key_server = TRUE; + } else if (kay->actor_priority + == key_server->key_server_priority) { + for (i = 0; i < 6; i++) { + if (kay->actor_sci.addr[i] + < key_server->sci.addr[i]) { + i_is_key_server = TRUE; + } + } + } + } + + if (!key_server && !i_is_key_server) { + participant->principal = FALSE; + participant->is_key_server = FALSE; + participant->is_elected = FALSE; + return 0; + } + + if (i_is_key_server) { + ieee802_1x_cp_set_electedself(kay->cp, TRUE); + if (os_memcmp(&kay->key_server_sci, &kay->actor_sci, + sizeof(kay->key_server_sci))) { + ieee802_1x_cp_signal_chgdserver(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + } + + participant->is_key_server = TRUE; + participant->principal = TRUE; + participant->new_sak = TRUE; + wpa_printf(MSG_DEBUG, "KaY: I is elected as key server"); + participant->to_dist_sak = FALSE; + participant->is_elected = TRUE; + + os_memcpy(&kay->key_server_sci, &kay->actor_sci, + sizeof(kay->key_server_sci)); + kay->key_server_priority = kay->actor_priority; + } + + if (key_server) { + ieee802_1x_cp_set_electedself(kay->cp, FALSE); + if (os_memcmp(&kay->key_server_sci, &key_server->sci, + sizeof(kay->key_server_sci))) { + ieee802_1x_cp_signal_chgdserver(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + } + + participant->is_key_server = FALSE; + participant->principal = TRUE; + participant->is_elected = TRUE; + + os_memcpy(&kay->key_server_sci, &key_server->sci, + sizeof(kay->key_server_sci)); + kay->key_server_priority = key_server->key_server_priority; + } + + return 0; +} + + +/** + * ieee802_1x_kay_decide_macsec_use - the key server determinate + * how to use MACsec: whether use MACsec and its capability + * protectFrames will be advised if the key server and one of its live peers are + * MACsec capable and one of those request MACsec protection + */ +static int +ieee802_1x_kay_decide_macsec_use( + struct ieee802_1x_mka_participant *participant) +{ + struct ieee802_1x_kay *kay = participant->kay; + struct ieee802_1x_kay_peer *peer; + enum macsec_cap less_capability; + Boolean has_peer; + + if (!participant->is_key_server) + return -1; + + /* key server self is MACsec-desired and requesting MACsec */ + if (!kay->macsec_desired) { + participant->advised_desired = FALSE; + return -1; + } + if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { + participant->advised_desired = FALSE; + return -1; + } + less_capability = kay->macsec_capable; + + /* at least one of peers is MACsec-desired and requesting MACsec */ + has_peer = FALSE; + dl_list_for_each(peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + if (!peer->macsec_desired) + continue; + + if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED) + continue; + + less_capability = (less_capability < peer->macsec_capbility) ? + less_capability : peer->macsec_capbility; + has_peer = TRUE; + } + + if (has_peer) { + participant->advised_desired = TRUE; + participant->advised_capability = less_capability; + kay->authenticated = FALSE; + kay->secured = TRUE; + kay->failed = FALSE; + ieee802_1x_cp_connect_secure(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + } else { + participant->advised_desired = FALSE; + participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED; + participant->to_use_sak = FALSE; + kay->authenticated = TRUE; + kay->secured = FALSE; + kay->failed = FALSE; + kay->ltx_kn = 0; + kay->ltx_an = 0; + kay->lrx_kn = 0; + kay->lrx_an = 0; + kay->otx_kn = 0; + kay->otx_an = 0; + kay->orx_kn = 0; + kay->orx_an = 0; + ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + } + + return 0; +} + +static const u8 pae_group_addr[ETH_ALEN] = { + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 +}; + + +/** + * ieee802_1x_kay_encode_mkpdu - + */ +static int +ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, + struct wpabuf *pbuf) +{ + unsigned int i; + struct ieee8023_hdr *ether_hdr; + struct ieee802_1x_hdr *eapol_hdr; + + ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr)); + os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest)); + os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr, + sizeof(ether_hdr->dest)); + ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL); + + eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr)); + eapol_hdr->version = EAPOL_VERSION; + eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA; + eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used); + + for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) { + if (mak_body_handler[i].body_present && + mak_body_handler[i].body_present(participant)) { + if (mak_body_handler[i].body_tx(participant, pbuf)) + return -1; + } + } + + return 0; +} + +/** + * ieee802_1x_participant_send_mkpdu - + */ +static int +ieee802_1x_participant_send_mkpdu( + struct ieee802_1x_mka_participant *participant) +{ + struct wpabuf *buf; + struct ieee802_1x_kay *kay = participant->kay; + size_t length = 0; + unsigned int i; + + wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU"); + length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); + for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) { + if (mak_body_handler[i].body_present && + mak_body_handler[i].body_present(participant)) + length += mak_body_handler[i].body_length(participant); + } + + buf = wpabuf_alloc(length); + if (!buf) { + wpa_printf(MSG_ERROR, "KaY: out of memory"); + return -1; + } + + if (ieee802_1x_kay_encode_mkpdu(participant, buf)) { + wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!"); + return -1; + } + + l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + + kay->active = TRUE; + participant->active = TRUE; + + return 0; +} + + +static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa); +/** + * ieee802_1x_participant_timer - + */ +static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct ieee802_1x_mka_participant *participant; + struct ieee802_1x_kay *kay; + struct ieee802_1x_kay_peer *peer, *pre_peer; + time_t now = time(NULL); + Boolean lp_changed; + struct receive_sc *rxsc, *pre_rxsc; + struct transmit_sa *txsa, *pre_txsa; + + participant = (struct ieee802_1x_mka_participant *)eloop_ctx; + kay = participant->kay; + if (participant->cak_life) { + if (now > participant->cak_life) { + kay->authenticated = FALSE; + kay->secured = FALSE; + kay->failed = TRUE; + ieee802_1x_kay_delete_mka(kay, &participant->ckn); + return; + } + } + + /* should delete MKA instance if there are not live peers + * when the MKA life elapsed since its creating */ + if (participant->mka_life) { + if (dl_list_empty(&participant->live_peers)) { + if (now > participant->mka_life) { + kay->authenticated = FALSE; + kay->secured = FALSE; + kay->failed = TRUE; + ieee802_1x_kay_delete_mka(kay, + &participant->ckn); + return; + } + } else { + participant->mka_life = 0; + } + } + + lp_changed = FALSE; + dl_list_for_each_safe(peer, pre_peer, &participant->live_peers, + struct ieee802_1x_kay_peer, list) { + if (now > peer->expire) { + wpa_printf(MSG_DEBUG, "KaY: Live peer removed"); + wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, + sizeof(peer->mi)); + wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); + dl_list_for_each_safe(rxsc, pre_rxsc, + &participant->rxsc_list, + struct receive_sc, list) { + if (os_memcmp(&rxsc->sci, &peer->sci, + sizeof(rxsc->sci)) == 0) { + secy_delete_receive_sc(kay, rxsc); + ieee802_1x_kay_deinit_receive_sc( + participant, rxsc); + } + } + dl_list_del(&peer->list); + os_free(peer); + lp_changed = TRUE; + } + } + + if (lp_changed) { + if (dl_list_empty(&participant->live_peers)) { + participant->advised_desired = FALSE; + participant->advised_capability = + MACSEC_CAP_NOT_IMPLEMENTED; + participant->to_use_sak = FALSE; + kay->authenticated = TRUE; + kay->secured = FALSE; + kay->failed = FALSE; + kay->ltx_kn = 0; + kay->ltx_an = 0; + kay->lrx_kn = 0; + kay->lrx_an = 0; + kay->otx_kn = 0; + kay->otx_an = 0; + kay->orx_kn = 0; + kay->orx_an = 0; + dl_list_for_each_safe(txsa, pre_txsa, + &participant->txsc->sa_list, + struct transmit_sa, list) { + secy_disable_transmit_sa(kay, txsa); + ieee802_1x_kay_deinit_transmit_sa(txsa); + } + + ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + } else { + ieee802_1x_kay_elect_key_server(participant); + ieee802_1x_kay_decide_macsec_use(participant); + } + } + + dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers, + struct ieee802_1x_kay_peer, list) { + if (now > peer->expire) { + wpa_printf(MSG_DEBUG, "KaY: Potential peer removed"); + wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, + sizeof(peer->mi)); + wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn); + dl_list_del(&peer->list); + os_free(peer); + } + } + + if (participant->new_sak) { + if (!ieee802_1x_kay_generate_new_sak(participant)) + participant->to_dist_sak = TRUE; + + participant->new_sak = FALSE; + } + + if (participant->retry_count < MAX_RETRY_CNT) { + ieee802_1x_participant_send_mkpdu(participant); + participant->retry_count++; + } + + eloop_register_timeout(MKA_HELLO_TIME / 1000, 0, + ieee802_1x_participant_timer, + participant, NULL); +} + + +/** + * ieee802_1x_kay_init_transmit_sa - + */ +static struct transmit_sa * +ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, + struct data_key *key) +{ + struct transmit_sa *psa; + + key->tx_latest = TRUE; + key->rx_latest = TRUE; + + psa = os_zalloc(sizeof(*psa)); + if (!psa) { + wpa_printf(MSG_ERROR, "%s: out of memory", __func__); + return NULL; + } + + if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 && + key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50) + psa->confidentiality = TRUE; + else + psa->confidentiality = FALSE; + + psa->an = an; + psa->pkey = key; + psa->next_pn = next_PN; + psa->sc = psc; + + os_get_time(&psa->created_time); + psa->in_use = FALSE; + + dl_list_add(&psc->sa_list, &psa->list); + wpa_printf(MSG_DEBUG, + "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)", + (int) an, next_PN, psc->channel); + + return psa; +} + + +/** + * ieee802_1x_kay_deinit_transmit_sa - + */ +static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) +{ + psa->pkey = NULL; + wpa_printf(MSG_DEBUG, + "KaY: Delete transmit SA(an: %d) of SC(channel: %d)", + psa->an, psa->sc->channel); + dl_list_del(&psa->list); + os_free(psa); +} + + +/** + * init_transmit_sc - + */ +static struct transmit_sc * +ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, + int channel) +{ + struct transmit_sc *psc; + + psc = os_zalloc(sizeof(*psc)); + if (!psc) { + wpa_printf(MSG_ERROR, "%s: out of memory", __func__); + return NULL; + } + os_memcpy(&psc->sci, sci, sizeof(psc->sci)); + psc->channel = channel; + + os_get_time(&psc->created_time); + psc->transmitting = FALSE; + psc->encoding_sa = FALSE; + psc->enciphering_sa = FALSE; + + dl_list_init(&psc->sa_list); + wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel); + wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci)); + + return psc; +} + + +/** + * ieee802_1x_kay_deinit_transmit_sc - + */ +static void +ieee802_1x_kay_deinit_transmit_sc( + struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc) +{ + struct transmit_sa *psa, *tmp; + + wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)", + psc->channel); + dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, + list) { + secy_disable_transmit_sa(participant->kay, psa); + ieee802_1x_kay_deinit_transmit_sa(psa); + } + + os_free(psc); +} + + +/****************** Interface between CP and KAY *********************/ +/** + * ieee802_1x_kay_set_latest_sa_attr - + */ +int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki, u8 lan, + Boolean ltx, Boolean lrx) +{ + struct ieee802_1x_mka_participant *principal; + + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + if (!lki) + os_memset(&principal->lki, 0, sizeof(principal->lki)); + else + os_memcpy(&principal->lki, lki, sizeof(principal->lki)); + + principal->lan = lan; + principal->ltx = ltx; + principal->lrx = lrx; + if (!lki) { + kay->ltx_kn = 0; + kay->lrx_kn = 0; + } else { + kay->ltx_kn = lki->kn; + kay->lrx_kn = lki->kn; + } + kay->ltx_an = lan; + kay->lrx_an = lan; + + return 0; +} + + +/** + * ieee802_1x_kay_set_old_sa_attr - + */ +int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *oki, + u8 oan, Boolean otx, Boolean orx) +{ + struct ieee802_1x_mka_participant *principal; + + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + if (!oki) + os_memset(&principal->oki, 0, sizeof(principal->oki)); + else + os_memcpy(&principal->oki, oki, sizeof(principal->oki)); + + principal->oan = oan; + principal->otx = otx; + principal->orx = orx; + + if (!oki) { + kay->otx_kn = 0; + kay->orx_kn = 0; + } else { + kay->otx_kn = oki->kn; + kay->orx_kn = oki->kn; + } + kay->otx_an = oan; + kay->orx_an = oan; + + return 0; +} + + +/** + * ieee802_1x_kay_create_sas - + */ +int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki) +{ + struct data_key *sa_key, *latest_sak; + struct ieee802_1x_mka_participant *principal; + struct receive_sc *rxsc; + struct receive_sa *rxsa; + struct transmit_sa *txsa; + + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + latest_sak = NULL; + dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) { + if (is_ki_equal(&sa_key->key_identifier, lki)) { + sa_key->rx_latest = TRUE; + sa_key->tx_latest = TRUE; + latest_sak = sa_key; + principal->to_use_sak = TRUE; + } else { + sa_key->rx_latest = FALSE; + sa_key->tx_latest = FALSE; + } + } + if (!latest_sak) { + wpa_printf(MSG_ERROR, "lki related sak not found"); + return -1; + } + + dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { + rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1, + latest_sak); + if (!rxsa) + return -1; + + secy_create_receive_sa(kay, rxsa); + } + + txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, + 1, latest_sak); + if (!txsa) + return -1; + + secy_create_transmit_sa(kay, txsa); + + + + return 0; +} + + +/** + * ieee802_1x_kay_delete_sas - + */ +int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *ki) +{ + struct data_key *sa_key, *pre_key; + struct transmit_sa *txsa, *pre_txsa; + struct receive_sa *rxsa, *pre_rxsa; + struct receive_sc *rxsc; + struct ieee802_1x_mka_participant *principal; + + wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__); + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + /* remove the transmit sa */ + dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list, + struct transmit_sa, list) { + if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { + secy_disable_transmit_sa(kay, txsa); + ieee802_1x_kay_deinit_transmit_sa(txsa); + } + } + + /* remove the receive sa */ + dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { + dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list, + struct receive_sa, list) { + if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) { + secy_disable_receive_sa(kay, rxsa); + ieee802_1x_kay_deinit_receive_sa(rxsa); + } + } + } + + /* remove the sak */ + dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list, + struct data_key, list) { + if (is_ki_equal(&sa_key->key_identifier, ki)) { + ieee802_1x_kay_deinit_data_key(sa_key); + break; + } + if (principal->new_key == sa_key) + principal->new_key = NULL; + } + + return 0; +} + + +/** + * ieee802_1x_kay_enable_tx_sas - + */ +int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki) +{ + struct ieee802_1x_mka_participant *principal; + struct transmit_sa *txsa; + + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa, + list) { + if (is_ki_equal(&txsa->pkey->key_identifier, lki)) { + txsa->in_use = TRUE; + secy_enable_transmit_sa(kay, txsa); + ieee802_1x_cp_set_usingtransmitas( + principal->kay->cp, TRUE); + ieee802_1x_cp_sm_step(principal->kay->cp); + } + } + + return 0; +} + + +/** + * ieee802_1x_kay_enable_rx_sas - + */ +int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki) +{ + struct ieee802_1x_mka_participant *principal; + struct receive_sa *rxsa; + struct receive_sc *rxsc; + + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { + dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) + { + if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) { + rxsa->in_use = TRUE; + secy_enable_receive_sa(kay, rxsa); + ieee802_1x_cp_set_usingreceivesas( + principal->kay->cp, TRUE); + ieee802_1x_cp_sm_step(principal->kay->cp); + } + } + } + + return 0; +} + + +/** + * ieee802_1x_kay_enable_new_info - + */ +int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) +{ + struct ieee802_1x_mka_participant *principal; + + principal = ieee802_1x_kay_get_principal_participant(kay); + if (!principal) + return -1; + + if (principal->retry_count < MAX_RETRY_CNT) { + ieee802_1x_participant_send_mkpdu(principal); + principal->retry_count++; + } + + return 0; +} + + +/** + * ieee802_1x_kay_cp_conf - + */ +int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay, + struct ieee802_1x_cp_conf *pconf) +{ + pconf->protect = kay->macsec_protect; + pconf->replay_protect = kay->macsec_replay_protect; + pconf->validate = kay->macsec_validate; + + return 0; +} + + +/** + * ieee802_1x_kay_alloc_cp_sm - + */ +static struct ieee802_1x_cp_sm * +ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay) +{ + struct ieee802_1x_cp_conf conf; + + os_memset(&conf, 0, sizeof(conf)); + conf.protect = kay->macsec_protect; + conf.replay_protect = kay->macsec_replay_protect; + conf.validate = kay->macsec_validate; + conf.replay_window = kay->macsec_replay_window; + + return ieee802_1x_cp_sm_init(kay, &conf); +} + + +/** + * ieee802_1x_kay_mkpdu_sanity_check - + * sanity check specified in clause 11.11.2 of IEEE802.1X-2010 + */ +static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, + const u8 *buf, size_t len) +{ + struct ieee8023_hdr *eth_hdr; + struct ieee802_1x_hdr *eapol_hdr; + struct ieee802_1x_mka_hdr *mka_hdr; + struct ieee802_1x_mka_basic_body *body; + size_t mka_msg_len; + struct ieee802_1x_mka_participant *participant; + size_t body_len; + u8 icv[MAX_ICV_LEN]; + u8 *msg_icv; + + eth_hdr = (struct ieee8023_hdr *) buf; + eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); + mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1); + + /* destination address should be not individual address */ + if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, + "KaY: ethernet destination address is not PAE group address"); + return -1; + } + + /* MKPDU should not less than 32 octets */ + mka_msg_len = be_to_host16(eapol_hdr->length); + if (mka_msg_len < 32) { + wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets"); + return -1; + } + /* MKPDU should multiple 4 octets */ + if ((mka_msg_len % 4) != 0) { + wpa_printf(MSG_MSGDUMP, + "KaY: MKPDU is not multiple of 4 octets"); + return -1; + } + + body = (struct ieee802_1x_mka_basic_body *) mka_hdr; + ieee802_1x_mka_dump_basic_body(body); + body_len = get_mka_param_body_len(body); + /* EAPOL-MKA body should comprise basic parameter set and ICV */ + if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) { + wpa_printf(MSG_ERROR, + "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV", + (int) mka_msg_len, (int) MKA_HDR_LEN, + (int) body_len, DEFAULT_ICV_LEN); + return -1; + } + + /* CKN should be owned by I */ + participant = ieee802_1x_kay_get_participant(kay, body->ckn); + if (!participant) { + wpa_printf(MSG_DEBUG, "CKN is not included in my CA"); + return -1; + } + + /* algorithm agility check */ + if (os_memcmp(body->algo_agility, mka_algo_agility, + sizeof(body->algo_agility)) != 0) { + wpa_printf(MSG_ERROR, + "KaY: peer's algorithm agility not supported for me"); + return -1; + } + + /* ICV check */ + /* + * The ICV will comprise the final octets of the packet body, whatever + * its size, not the fixed length 16 octets, indicated by the EAPOL + * packet body length. + */ + if (mka_alg_tbl[kay->mka_algindex].icv_hash( + participant->ick.key, + buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) { + wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed"); + return -1; + } + msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr, + mka_msg_len); + + if (msg_icv) { + if (os_memcmp_const(msg_icv, icv, + mka_alg_tbl[kay->mka_algindex].icv_len) != + 0) { + wpa_printf(MSG_ERROR, + "KaY: Computed ICV is not equal to Received ICV"); + return -1; + } + } else { + wpa_printf(MSG_ERROR, "KaY: No ICV"); + return -1; + } + + return 0; +} + + +/** + * ieee802_1x_kay_decode_mkpdu - + */ +static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, + const u8 *buf, size_t len) +{ + struct ieee802_1x_mka_participant *participant; + struct ieee802_1x_mka_hdr *hdr; + size_t body_len; + size_t left_len; + int body_type; + int i; + const u8 *pos; + Boolean my_included; + Boolean handled[256]; + + if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) + return -1; + + /* handle basic parameter set */ + pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr); + left_len = len - sizeof(struct ieee8023_hdr) - + sizeof(struct ieee802_1x_hdr); + participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len); + if (!participant) + return -1; + + /* to skip basic parameter set */ + hdr = (struct ieee802_1x_mka_hdr *) pos; + body_len = get_mka_param_body_len(hdr); + pos += body_len + MKA_HDR_LEN; + left_len -= body_len + MKA_HDR_LEN; + + /* check i am in the peer's peer list */ + my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len); + if (my_included) { + /* accept the peer as live peer */ + if (!ieee802_1x_kay_is_in_peer( + participant, + participant->current_peer_id.mi)) { + if (!ieee802_1x_kay_create_live_peer( + participant, + participant->current_peer_id.mi, + participant->current_peer_id.mn)) + return -1; + ieee802_1x_kay_elect_key_server(participant); + ieee802_1x_kay_decide_macsec_use(participant); + } + if (ieee802_1x_kay_is_in_potential_peer( + participant, participant->current_peer_id.mi)) { + ieee802_1x_kay_move_live_peer( + participant, participant->current_peer_id.mi, + participant->current_peer_id.mn); + ieee802_1x_kay_elect_key_server(participant); + ieee802_1x_kay_decide_macsec_use(participant); + } + } + + /* + * Handle other parameter set than basic parameter set. + * Each parameter set should be present only once. + */ + for (i = 0; i < 256; i++) + handled[i] = FALSE; + + handled[0] = TRUE; + while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { + hdr = (struct ieee802_1x_mka_hdr *) pos; + body_len = get_mka_param_body_len(hdr); + body_type = get_mka_param_body_type(hdr); + + if (body_type == MKA_ICV_INDICATOR) + return 0; + + if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV", + (int) left_len, (int) MKA_HDR_LEN, + (int) body_len, DEFAULT_ICV_LEN); + goto next_para_set; + } + + if (handled[body_type]) + goto next_para_set; + + handled[body_type] = TRUE; + if (mak_body_handler[body_type].body_rx) { + mak_body_handler[body_type].body_rx + (participant, pos, left_len); + } else { + wpa_printf(MSG_ERROR, + "The type %d not supported in this MKA version %d", + body_type, MKA_VERSION_ID); + } + +next_para_set: + pos += body_len + MKA_HDR_LEN; + left_len -= body_len + MKA_HDR_LEN; + } + + kay->active = TRUE; + participant->retry_count = 0; + participant->active = TRUE; + + return 0; +} + + + +static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct ieee802_1x_kay *kay = ctx; + struct ieee8023_hdr *eth_hdr; + struct ieee802_1x_hdr *eapol_hdr; + + /* must contain at least ieee8023_hdr + ieee802_1x_hdr */ + if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) { + wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)", + (unsigned long) len); + return; + } + + eth_hdr = (struct ieee8023_hdr *) buf; + eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); + if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) + + ntohs(eapol_hdr->length)) { + wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)", + (unsigned long) len, + (unsigned long) ntohs(eapol_hdr->length)); + return; + } + + if (eapol_hdr->version < EAPOL_VERSION) { + wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA", + eapol_hdr->version); + return; + } + if (ntohs(eth_hdr->ethertype) != ETH_P_PAE || + eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) + return; + + wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len); + if (dl_list_empty(&kay->participant_list)) { + wpa_printf(MSG_ERROR, "KaY: no MKA participant instance"); + return; + } + + ieee802_1x_kay_decode_mkpdu(kay, buf, len); +} + + +/** + * ieee802_1x_kay_init - + */ +struct ieee802_1x_kay * +ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, + const char *ifname, const u8 *addr) +{ + struct ieee802_1x_kay *kay; + + kay = os_zalloc(sizeof(*kay)); + if (!kay) { + wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + return NULL; + } + + kay->ctx = ctx; + + kay->enable = TRUE; + kay->active = FALSE; + + kay->authenticated = FALSE; + kay->secured = FALSE; + kay->failed = FALSE; + kay->policy = policy; + + os_strlcpy(kay->if_name, ifname, IFNAMSIZ); + os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); + kay->actor_sci.port = 0x0001; + kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER; + + /* While actor acts as a key server, shall distribute sakey */ + kay->dist_kn = 1; + kay->dist_an = 0; + kay->dist_time = 0; + + kay->pn_exhaustion = PENDING_PN_EXHAUSTION; + kay->macsec_csindex = DEFAULT_CS_INDEX; + kay->mka_algindex = DEFAULT_MKA_ALG_INDEX; + kay->mka_version = MKA_VERSION_ID; + + os_memcpy(kay->algo_agility, mka_algo_agility, + sizeof(kay->algo_agility)); + + dl_list_init(&kay->participant_list); + + if (policy == DO_NOT_SECURE) { + kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; + kay->macsec_desired = FALSE; + kay->macsec_protect = FALSE; + kay->macsec_validate = Disabled; + kay->macsec_replay_protect = FALSE; + kay->macsec_replay_window = 0; + kay->macsec_confidentiality = CONFIDENTIALITY_NONE; + } else { + kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50; + kay->macsec_desired = TRUE; + kay->macsec_protect = TRUE; + kay->macsec_validate = Strict; + kay->macsec_replay_protect = FALSE; + kay->macsec_replay_window = 0; + kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; + } + + wpa_printf(MSG_DEBUG, "KaY: state machine created"); + + /* Initialize the SecY must be prio to CP, as CP will control SecY */ + secy_init_macsec(kay); + secy_get_available_transmit_sc(kay, &kay->sc_ch); + + wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); + + /* init CP */ + kay->cp = ieee802_1x_kay_alloc_cp_sm(kay); + if (kay->cp == NULL) { + ieee802_1x_kay_deinit(kay); + return NULL; + } + + if (policy == DO_NOT_SECURE) { + ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); + } else { + kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE, + kay_l2_receive, kay, 1); + if (kay->l2_mka == NULL) { + wpa_printf(MSG_WARNING, + "KaY: Failed to initialize L2 packet processing for MKA packet"); + ieee802_1x_kay_deinit(kay); + return NULL; + } + } + + return kay; +} + + +/** + * ieee802_1x_kay_deinit - + */ +void +ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) +{ + struct ieee802_1x_mka_participant *participant; + + if (!kay) + return; + + wpa_printf(MSG_DEBUG, "KaY: state machine removed"); + + while (!dl_list_empty(&kay->participant_list)) { + participant = dl_list_entry(kay->participant_list.next, + struct ieee802_1x_mka_participant, + list); + ieee802_1x_kay_delete_mka(kay, &participant->ckn); + } + + ieee802_1x_cp_sm_deinit(kay->cp); + secy_deinit_macsec(kay); + + if (kay->l2_mka) { + l2_packet_deinit(kay->l2_mka); + kay->l2_mka = NULL; + } + + os_free(kay->ctx); + os_free(kay); +} + + +/** + * ieee802_1x_kay_create_mka - + */ +struct ieee802_1x_mka_participant * +ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, + struct mka_key *cak, u32 life, + enum mka_created_mode mode, Boolean is_authenticator) +{ + struct ieee802_1x_mka_participant *participant; + unsigned int usecs; + + if (!kay || !ckn || !cak) { + wpa_printf(MSG_ERROR, "KaY: ckn or cak is null"); + return NULL; + } + + if (cak->len != mka_alg_tbl[kay->mka_algindex].cak_len) { + wpa_printf(MSG_ERROR, "KaY: CAK length not follow key schema"); + return NULL; + } + if (ckn->len > MAX_CKN_LEN) { + wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)"); + return NULL; + } + if (!kay->enable) { + wpa_printf(MSG_ERROR, "KaY: Now is at disable state"); + return NULL; + } + + participant = os_zalloc(sizeof(*participant)); + if (!participant) { + wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + return NULL; + } + + participant->ckn.len = ckn->len; + os_memcpy(participant->ckn.name, ckn->name, ckn->len); + participant->cak.len = cak->len; + os_memcpy(participant->cak.key, cak->key, cak->len); + if (life) + participant->cak_life = life + time(NULL); + + switch (mode) { + case EAP_EXCHANGE: + if (is_authenticator) { + participant->is_obliged_key_server = TRUE; + participant->can_be_key_server = TRUE; + participant->is_key_server = TRUE; + participant->principal = TRUE; + + os_memcpy(&kay->key_server_sci, &kay->actor_sci, + sizeof(kay->key_server_sci)); + kay->key_server_priority = kay->actor_priority; + participant->is_elected = TRUE; + } else { + participant->is_obliged_key_server = FALSE; + participant->can_be_key_server = FALSE; + participant->is_key_server = FALSE; + participant->is_elected = TRUE; + } + break; + + default: + participant->is_obliged_key_server = FALSE; + participant->can_be_key_server = TRUE; + participant->is_key_server = FALSE; + participant->is_elected = FALSE; + break; + } + + participant->cached = FALSE; + + participant->active = FALSE; + participant->participant = FALSE; + participant->retain = FALSE; + participant->activate = DEFAULT; + + if (participant->is_key_server) + participant->principal = TRUE; + + dl_list_init(&participant->live_peers); + dl_list_init(&participant->potential_peers); + + participant->retry_count = 0; + participant->kay = kay; + + if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + goto fail; + participant->mn = 0; + + participant->lrx = FALSE; + participant->ltx = FALSE; + participant->orx = FALSE; + participant->otx = FALSE; + participant->to_dist_sak = FALSE; + participant->to_use_sak = FALSE; + participant->new_sak = FALSE; + dl_list_init(&participant->sak_list); + participant->new_key = NULL; + dl_list_init(&participant->rxsc_list); + participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci, + kay->sc_ch); + secy_cp_control_protect_frames(kay, kay->macsec_protect); + secy_cp_control_replay(kay, kay->macsec_replay_protect, + kay->macsec_replay_window); + secy_create_transmit_sc(kay, participant->txsc); + + /* to derive KEK from CAK and CKN */ + participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len; + if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key, + participant->ckn.name, + participant->ckn.len, + participant->kek.key)) { + wpa_printf(MSG_ERROR, "KaY: Derived KEK failed"); + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK", + participant->kek.key, participant->kek.len); + + /* to derive ICK from CAK and CKN */ + participant->ick.len = mka_alg_tbl[kay->mka_algindex].ick_len; + if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key, + participant->ckn.name, + participant->ckn.len, + participant->ick.key)) { + wpa_printf(MSG_ERROR, "KaY: Derived ICK failed"); + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK", + participant->ick.key, participant->ick.len); + + dl_list_add(&kay->participant_list, &participant->list); + wpa_hexdump(MSG_DEBUG, "KaY: Participant created:", + ckn->name, ckn->len); + + usecs = os_random() % (MKA_HELLO_TIME * 1000); + eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, + participant, NULL); + participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + + usecs / 1000000; + + return participant; + +fail: + os_free(participant); + return NULL; +} + + +/** + * ieee802_1x_kay_delete_mka - + */ +void +ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) +{ + struct ieee802_1x_mka_participant *participant; + struct ieee802_1x_kay_peer *peer; + struct data_key *sak; + struct receive_sc *rxsc; + + if (!kay || !ckn) + return; + + wpa_printf(MSG_DEBUG, "KaY: participant removed"); + + /* get the participant */ + participant = ieee802_1x_kay_get_participant(kay, ckn->name); + if (!participant) { + wpa_hexdump(MSG_DEBUG, "KaY: participant is not found", + ckn->name, ckn->len); + return; + } + + dl_list_del(&participant->list); + + /* remove live peer */ + while (!dl_list_empty(&participant->live_peers)) { + peer = dl_list_entry(participant->live_peers.next, + struct ieee802_1x_kay_peer, list); + dl_list_del(&peer->list); + os_free(peer); + } + + /* remove potential peer */ + while (!dl_list_empty(&participant->potential_peers)) { + peer = dl_list_entry(participant->potential_peers.next, + struct ieee802_1x_kay_peer, list); + dl_list_del(&peer->list); + os_free(peer); + } + + /* remove sak */ + while (!dl_list_empty(&participant->sak_list)) { + sak = dl_list_entry(participant->sak_list.next, + struct data_key, list); + dl_list_del(&sak->list); + os_free(sak->key); + os_free(sak); + } + while (!dl_list_empty(&participant->rxsc_list)) { + rxsc = dl_list_entry(participant->rxsc_list.next, + struct receive_sc, list); + secy_delete_receive_sc(kay, rxsc); + ieee802_1x_kay_deinit_receive_sc(participant, rxsc); + } + secy_delete_transmit_sc(kay, participant->txsc); + ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc); + + os_memset(&participant->cak, 0, sizeof(participant->cak)); + os_memset(&participant->kek, 0, sizeof(participant->kek)); + os_memset(&participant->ick, 0, sizeof(participant->ick)); + os_free(participant); +} + + +/** + * ieee802_1x_kay_mka_participate - + */ +void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, + struct mka_key_name *ckn, + Boolean status) +{ + struct ieee802_1x_mka_participant *participant; + + if (!kay || !ckn) + return; + + participant = ieee802_1x_kay_get_participant(kay, ckn->name); + if (!participant) + return; + + participant->active = status; +} + + +/** + * ieee802_1x_kay_new_sak - + */ +int +ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay) +{ + struct ieee802_1x_mka_participant *participant; + + if (!kay) + return -1; + + participant = ieee802_1x_kay_get_principal_participant(kay); + if (!participant) + return -1; + + participant->new_sak = TRUE; + wpa_printf(MSG_DEBUG, "KaY: new SAK signal"); + + return 0; +} + + +/** + * ieee802_1x_kay_change_cipher_suite - + */ +int +ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index) +{ + struct ieee802_1x_mka_participant *participant; + + if (!kay) + return -1; + + if ((unsigned int) cs_index >= CS_TABLE_SIZE) { + wpa_printf(MSG_ERROR, + "KaY: Configured cipher suite index is out of range"); + return -1; + } + if (kay->macsec_csindex == cs_index) + return -2; + + if (cs_index == 0) + kay->macsec_desired = FALSE; + + kay->macsec_csindex = cs_index; + kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable; + + participant = ieee802_1x_kay_get_principal_participant(kay); + if (participant) { + wpa_printf(MSG_INFO, "KaY: Cipher Suite changed"); + participant->new_sak = TRUE; + } + + return 0; +} diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h new file mode 100644 index 000000000000..064417ea51ad --- /dev/null +++ b/src/pae/ieee802_1x_kay.h @@ -0,0 +1,194 @@ +/* + * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_KAY_H +#define IEEE802_1X_KAY_H + +#include "utils/list.h" +#include "common/defs.h" +#include "common/ieee802_1x_defs.h" + +struct macsec_init_params; +struct ieee802_1x_cp_conf; + +#define MI_LEN 12 +#define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */ +#define MAX_CKN_LEN 32 /* 32 bytes, 256 bits */ + +/* MKA timer, unit: millisecond */ +#define MKA_HELLO_TIME 2000 +#define MKA_LIFE_TIME 6000 +#define MKA_SAK_RETIRE_TIME 3000 + +struct ieee802_1x_mka_ki { + u8 mi[MI_LEN]; + u32 kn; +}; + +struct ieee802_1x_mka_sci { + u8 addr[ETH_ALEN]; + u16 port; +}; + +struct mka_key { + u8 key[MAX_KEY_LEN]; + size_t len; +}; + +struct mka_key_name { + u8 name[MAX_CKN_LEN]; + size_t len; +}; + +enum mka_created_mode { + PSK, + EAP_EXCHANGE, + DISTRIBUTED, + CACHED, +}; + +struct ieee802_1x_kay_ctx { + /* pointer to arbitrary upper level context */ + void *ctx; + + /* abstract wpa driver interface */ + int (*macsec_init)(void *ctx, struct macsec_init_params *params); + int (*macsec_deinit)(void *ctx); + int (*enable_protect_frames)(void *ctx, Boolean enabled); + int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window); + int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len); + int (*enable_controlled_port)(void *ctx, Boolean enabled); + int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an, + u32 *lowest_pn); + int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an, + u32 *next_pn); + int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn); + int (*get_available_receive_sc)(void *ctx, u32 *channel); + int (*create_receive_sc)(void *ctx, u32 channel, + struct ieee802_1x_mka_sci *sci, + enum validate_frames vf, + enum confidentiality_offset co); + int (*delete_receive_sc)(void *ctx, u32 channel); + int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn, + const u8 *sak); + int (*enable_receive_sa)(void *ctx, u32 channel, u8 an); + int (*disable_receive_sa)(void *ctx, u32 channel, u8 an); + int (*get_available_transmit_sc)(void *ctx, u32 *channel); + int (*create_transmit_sc)(void *ctx, u32 channel, + const struct ieee802_1x_mka_sci *sci, + enum confidentiality_offset co); + int (*delete_transmit_sc)(void *ctx, u32 channel); + int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn, + Boolean confidentiality, const u8 *sak); + int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an); + int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an); +}; + +struct ieee802_1x_kay { + Boolean enable; + Boolean active; + + Boolean authenticated; + Boolean secured; + Boolean failed; + + struct ieee802_1x_mka_sci actor_sci; + u8 actor_priority; + struct ieee802_1x_mka_sci key_server_sci; + u8 key_server_priority; + + enum macsec_cap macsec_capable; + Boolean macsec_desired; + Boolean macsec_protect; + Boolean macsec_replay_protect; + u32 macsec_replay_window; + enum validate_frames macsec_validate; + enum confidentiality_offset macsec_confidentiality; + + u32 ltx_kn; + u8 ltx_an; + u32 lrx_kn; + u8 lrx_an; + + u32 otx_kn; + u8 otx_an; + u32 orx_kn; + u8 orx_an; + + /* not defined in IEEE802.1X */ + struct ieee802_1x_kay_ctx *ctx; + Boolean is_key_server; + Boolean is_obliged_key_server; + char if_name[IFNAMSIZ]; + + int macsec_csindex; /* MACsec cipher suite table index */ + int mka_algindex; /* MKA alg table index */ + + u32 dist_kn; + u8 dist_an; + time_t dist_time; + + u8 mka_version; + u8 algo_agility[4]; + u32 sc_ch; + + u32 pn_exhaustion; + Boolean port_enable; + Boolean rx_enable; + Boolean tx_enable; + + struct dl_list participant_list; + enum macsec_policy policy; + + struct ieee802_1x_cp_sm *cp; + + struct l2_packet_data *l2_mka; + + enum validate_frames vf; + enum confidentiality_offset co; +}; + + +struct ieee802_1x_kay * +ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, + const char *ifname, const u8 *addr); +void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay); + +struct ieee802_1x_mka_participant * +ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, + struct mka_key_name *ckn, struct mka_key *cak, + u32 life, enum mka_created_mode mode, + Boolean is_authenticator); +void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, + struct mka_key_name *ckn); +void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, + struct mka_key_name *ckn, + Boolean status); +int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay); +int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, + int cs_index); + +int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki, u8 lan, + Boolean ltx, Boolean lrx); +int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *oki, + u8 oan, Boolean otx, Boolean orx); +int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki); +int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *ki); +int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki); +int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, + struct ieee802_1x_mka_ki *lki); +int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); +int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay, + struct ieee802_1x_cp_conf *pconf); + +#endif /* IEEE802_1X_KAY_H */ diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h new file mode 100644 index 000000000000..bdad3a5beb13 --- /dev/null +++ b/src/pae/ieee802_1x_kay_i.h @@ -0,0 +1,419 @@ +/* + * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_KAY_I_H +#define IEEE802_1X_KAY_I_H + +#include "utils/list.h" +#include "common/defs.h" +#include "common/ieee802_1x_defs.h" + +#define MKA_VERSION_ID 1 + +/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */ +enum mka_packet_type { + MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID, + MKA_LIVE_PEER_LIST = 1, + MKA_POTENTIAL_PEER_LIST = 2, + MKA_SAK_USE = 3, + MKA_DISTRIBUTED_SAK = 4, + MKA_DISTRIBUTED_CAK = 5, + MKA_KMD = 6, + MKA_ANNOUNCEMENT = 7, + MKA_ICV_INDICATOR = 255 +}; + +#define ICV_LEN 16 /* 16 bytes */ +#define SAK_WRAPPED_LEN 24 +/* KN + Wrapper SAK */ +#define DEFAULT_DIS_SAK_BODY_LENGTH (SAK_WRAPPED_LEN + 4) +#define MAX_RETRY_CNT 5 + +struct ieee802_1x_kay; + +struct ieee802_1x_mka_peer_id { + u8 mi[MI_LEN]; + u32 mn; +}; + +struct ieee802_1x_kay_peer { + struct ieee802_1x_mka_sci sci; + u8 mi[MI_LEN]; + u32 mn; + time_t expire; + Boolean is_key_server; + u8 key_server_priority; + Boolean macsec_desired; + enum macsec_cap macsec_capbility; + Boolean sak_used; + struct dl_list list; +}; + +struct key_conf { + u8 *key; + struct ieee802_1x_mka_ki ki; + enum confidentiality_offset offset; + u8 an; + Boolean tx; + Boolean rx; + int key_len; /* unit: byte */ +}; + +struct data_key { + u8 *key; + int key_len; + struct ieee802_1x_mka_ki key_identifier; + enum confidentiality_offset confidentiality_offset; + u8 an; + Boolean transmits; + Boolean receives; + struct os_time created_time; + u32 next_pn; + + /* not defined data */ + Boolean rx_latest; + Boolean tx_latest; + + int user; /* FIXME: to indicate if it can be delete safely */ + + struct dl_list list; +}; + +/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct transmit_sc { + struct ieee802_1x_mka_sci sci; /* const SCI sci */ + Boolean transmitting; /* bool transmitting (read only) */ + + struct os_time created_time; /* Time createdTime */ + + u8 encoding_sa; /* AN encodingSA (read only) */ + u8 enciphering_sa; /* AN encipheringSA (read only) */ + + /* not defined data */ + unsigned int channel; + + struct dl_list list; + struct dl_list sa_list; +}; + +/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct transmit_sa { + Boolean in_use; /* bool inUse (read only) */ + u32 next_pn; /* PN nextPN (read only) */ + struct os_time created_time; /* Time createdTime */ + + Boolean enable_transmit; /* bool EnableTransmit */ + + u8 an; + Boolean confidentiality; + struct data_key *pkey; + + struct transmit_sc *sc; + struct dl_list list; /* list entry in struct transmit_sc::sa_list */ +}; + +/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct receive_sc { + struct ieee802_1x_mka_sci sci; /* const SCI sci */ + Boolean receiving; /* bool receiving (read only) */ + + struct os_time created_time; /* Time createdTime */ + + unsigned int channel; + + struct dl_list list; + struct dl_list sa_list; +}; + +/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct receive_sa { + Boolean enable_receive; /* bool enableReceive */ + Boolean in_use; /* bool inUse (read only) */ + + u32 next_pn; /* PN nextPN (read only) */ + u32 lowest_pn; /* PN lowestPN (read only) */ + u8 an; + struct os_time created_time; + + struct data_key *pkey; + struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */ + + struct dl_list list; +}; + +struct macsec_ciphersuite { + u8 id[CS_ID_LEN]; + char name[32]; + enum macsec_cap capable; + int sak_len; /* unit: byte */ + + u32 index; +}; + +struct mka_alg { + u8 parameter[4]; + size_t cak_len; + size_t kek_len; + size_t ick_len; + size_t icv_len; + + int (*cak_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, u8 *cak); + int (*ckn_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, + const u8 *sid, size_t sid_len, u8 *ckn); + int (*kek_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *kek); + int (*ick_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *ick); + int (*icv_hash)(const u8 *ick, const u8 *msg, size_t msg_len, u8 *icv); + + int index; /* index for configuring */ +}; + +#define DEFAULT_MKA_ALG_INDEX 0 + +/* See IEEE Std 802.1X-2010, 9.16 MKA management */ +struct ieee802_1x_mka_participant { + /* used for active and potential participant */ + struct mka_key_name ckn; + struct mka_key cak; + Boolean cached; + + /* used by management to monitor and control activation */ + Boolean active; + Boolean participant; + Boolean retain; + + enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; + + /* used for active participant */ + Boolean principal; + struct dl_list live_peers; + struct dl_list potential_peers; + + /* not defined in IEEE 802.1X */ + struct dl_list list; + + struct mka_key kek; + struct mka_key ick; + + struct ieee802_1x_mka_ki lki; + u8 lan; + Boolean ltx; + Boolean lrx; + + struct ieee802_1x_mka_ki oki; + u8 oan; + Boolean otx; + Boolean orx; + + Boolean is_key_server; + Boolean is_obliged_key_server; + Boolean can_be_key_server; + Boolean is_elected; + + struct dl_list sak_list; + struct dl_list rxsc_list; + + struct transmit_sc *txsc; + + u8 mi[MI_LEN]; + u32 mn; + + struct ieee802_1x_mka_peer_id current_peer_id; + struct ieee802_1x_mka_sci current_peer_sci; + time_t cak_life; + time_t mka_life; + Boolean to_dist_sak; + Boolean to_use_sak; + Boolean new_sak; + + Boolean advised_desired; + enum macsec_cap advised_capability; + + struct data_key *new_key; + u32 retry_count; + + struct ieee802_1x_kay *kay; +}; + +struct ieee802_1x_mka_hdr { + /* octet 1 */ + u32 type:8; + /* octet 2 */ + u32 reserve:8; + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 length:4; + u32 reserve1:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 reserve1:4; + u32 length:4; +#else +#error "Please fix " +#endif + /* octet 4 */ + u32 length1:8; +}; + +#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) + +struct ieee802_1x_mka_basic_body { + /* octet 1 */ + u32 version:8; + /* octet 2 */ + u32 priority:8; + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 length:4; + u32 macsec_capbility:2; + u32 macsec_desired:1; + u32 key_server:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 key_server:1; + u32 macsec_desired:1; + u32 macsec_capbility:2; + u32 length:4; +#endif + /* octet 4 */ + u32 length1:8; + + struct ieee802_1x_mka_sci actor_sci; + u8 actor_mi[MI_LEN]; + u32 actor_mn; + u8 algo_agility[4]; + + /* followed by CAK Name*/ + u8 ckn[0]; +}; + +struct ieee802_1x_mka_peer_body { + /* octet 1 */ + u32 type:8; + /* octet 2 */ + u32 reserve:8; + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 length:4; + u32 reserve1:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 reserve1:4; + u32 length:4; +#endif + /* octet 4 */ + u32 length1:8; + + u8 peer[0]; + /* followed by Peers */ +}; + +struct ieee802_1x_mka_sak_use_body { + /* octet 1 */ + u32 type:8; + /* octet 2 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 orx:1; + u32 otx:1; + u32 oan:2; + u32 lrx:1; + u32 ltx:1; + u32 lan:2; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 lan:2; + u32 ltx:1; + u32 lrx:1; + u32 oan:2; + u32 otx:1; + u32 orx:1; +#endif + + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 length:4; + u32 delay_protect:1; + u32 reserve:1; + u32 prx:1; + u32 ptx:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 ptx:1; + u32 prx:1; + u32 reserve:1; + u32 delay_protect:1; + u32 length:4; +#endif + + /* octet 4 */ + u32 length1:8; + + /* octet 5 - 16 */ + u8 lsrv_mi[MI_LEN]; + /* octet 17 - 20 */ + u32 lkn; + /* octet 21 - 24 */ + u32 llpn; + + /* octet 25 - 36 */ + u8 osrv_mi[MI_LEN]; + /* octet 37 - 40 */ + u32 okn; + /* octet 41 - 44 */ + u32 olpn; +}; + + +struct ieee802_1x_mka_dist_sak_body { + /* octet 1 */ + u32 type:8; + /* octet 2 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 reserve:4; + u32 confid_offset:2; + u32 dan:2; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 dan:2; + u32 confid_offset:2; + u32 reserve:4; +#endif + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 length:4; + u32 reserve1:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 reserve1:4; + u32 length:4; +#endif + /* octet 4 */ + u32 length1:8; + /* octet 5 - 8 */ + u32 kn; + + /* for GCM-AES-128: octet 9-32: SAK + * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK + */ + u8 sak[0]; +}; + + +struct ieee802_1x_mka_icv_body { + /* octet 1 */ + u32 type:8; + /* octet 2 */ + u32 reserve:8; + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u32 length:4; + u32 reserve1:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + u32 reserve1:4; + u32 length:4; +#endif + /* octet 4 */ + u32 length1:8; + + /* octet 5 - */ + u8 icv[0]; +}; + +#endif /* IEEE802_1X_KAY_I_H */ diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c new file mode 100644 index 000000000000..9a8d923d14f1 --- /dev/null +++ b/src/pae/ieee802_1x_key.c @@ -0,0 +1,189 @@ +/* + * IEEE 802.1X-2010 Key Hierarchy + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2 +*/ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "ieee802_1x_key.h" + + +static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out) +{ + if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) { + os_memcpy(out, mac1, ETH_ALEN); + os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN); + } else { + os_memcpy(out, mac2, ETH_ALEN); + os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN); + } +} + + +/* IEEE Std 802.1X-2010, 6.2.1 KDF */ +static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, + int ctx_bits, int ret_bits, u8 *ret) +{ + const int h = 128; + const int r = 8; + int i, n; + int lab_len, ctx_len, ret_len, buf_len; + u8 *buf; + + lab_len = os_strlen(label); + ctx_len = (ctx_bits + 7) / 8; + ret_len = ((ret_bits & 0xffff) + 7) / 8; + buf_len = lab_len + ctx_len + 4; + + os_memset(ret, 0, ret_len); + + n = (ret_bits + h - 1) / h; + if (n > ((0x1 << r) - 1)) + return -1; + + buf = os_zalloc(buf_len); + if (buf == NULL) + return -1; + + os_memcpy(buf + 1, label, lab_len); + os_memcpy(buf + lab_len + 2, context, ctx_len); + WPA_PUT_BE16(&buf[buf_len - 2], ret_bits); + + for (i = 0; i < n; i++) { + buf[0] = (u8) (i + 1); + if (omac1_aes_128(kdk, buf, buf_len, ret)) { + os_free(buf); + return -1; + } + ret = ret + h / 8; + } + os_free(buf); + return 0; +} + + +/********** AES-CMAC-128 **********/ +/** + * ieee802_1x_cak_128bits_aes_cmac + * + * IEEE Std 802.1X-2010, 6.2.2 + * CAK = KDF(Key, Label, mac1 | mac2, CAKlength) + */ +int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1, + const u8 *mac2, u8 *cak) +{ + u8 context[2 * ETH_ALEN]; + + joint_two_mac(mac1, mac2, context); + return aes_kdf_128(msk, "IEEE8021 EAP CAK", + context, sizeof(context) * 8, 128, cak); +} + + +/** + * ieee802_1x_ckn_128bits_aes_cmac + * + * IEEE Std 802.1X-2010, 6.2.2 + * CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength) + */ +int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1, + const u8 *mac2, const u8 *sid, + size_t sid_bytes, u8 *ckn) +{ + int res; + u8 *context; + size_t ctx_len = sid_bytes + ETH_ALEN * 2; + + context = os_zalloc(ctx_len); + if (!context) { + wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__); + return -1; + } + os_memcpy(context, sid, sid_bytes); + joint_two_mac(mac1, mac2, context + sid_bytes); + + res = aes_kdf_128(msk, "IEEE8021 EAP CKN", context, ctx_len * 8, + 128, ckn); + os_free(context); + return res; +} + + +/** + * ieee802_1x_kek_128bits_aes_cmac + * + * IEEE Std 802.1X-2010, 9.3.3 + * KEK = KDF(Key, Label, Keyid, KEKLength) + */ +int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn, + size_t ckn_bytes, u8 *kek) +{ + u8 context[16]; + + /* First 16 octets of CKN, with null octets appended to pad if needed */ + os_memset(context, 0, sizeof(context)); + os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16); + + return aes_kdf_128(cak, "IEEE8021 KEK", context, sizeof(context) * 8, + 128, kek); +} + + +/** + * ieee802_1x_ick_128bits_aes_cmac + * + * IEEE Std 802.1X-2010, 9.3.3 + * ICK = KDF(Key, Label, Keyid, ICKLength) + */ +int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn, + size_t ckn_bytes, u8 *ick) +{ + u8 context[16]; + + /* First 16 octets of CKN, with null octets appended to pad if needed */ + os_memset(context, 0, sizeof(context)); + os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16); + + return aes_kdf_128(cak, "IEEE8021 ICK", context, sizeof(context) * 8, + 128, ick); +} + + +/** + * ieee802_1x_icv_128bits_aes_cmac + * + * IEEE Std 802.1X-2010, 9.4.1 + * ICV = AES-CMAC(ICK, M, 128) + */ +int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg, + size_t msg_bytes, u8 *icv) +{ + if (omac1_aes_128(ick, msg, msg_bytes, icv)) { + wpa_printf(MSG_ERROR, "MKA: omac1_aes_128 failed"); + return -1; + } + return 0; +} + + +/** + * ieee802_1x_sak_128bits_aes_cmac + * + * IEEE Std 802.1X-2010, 9.8.1 + * SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength) + */ +int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx, + size_t ctx_bytes, u8 *sak) +{ + return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak); +} diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h new file mode 100644 index 000000000000..ea318ea4dde3 --- /dev/null +++ b/src/pae/ieee802_1x_key.h @@ -0,0 +1,26 @@ +/* + * IEEE 802.1X-2010 Key Hierarchy + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_KEY_H +#define IEEE802_1X_KEY_H + +int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1, + const u8 *mac2, u8 *cak); +int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1, + const u8 *mac2, const u8 *sid, + size_t sid_bytes, u8 *ckn); +int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn, + size_t ckn_bytes, u8 *kek); +int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn, + size_t ckn_bytes, u8 *ick); +int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg, + size_t msg_bytes, u8 *icv); +int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx, + size_t ctx_bytes, u8 *sak); + +#endif /* IEEE802_1X_KEY_H */ diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c new file mode 100644 index 000000000000..fbe05dc35d7c --- /dev/null +++ b/src/pae/ieee802_1x_secy_ops.c @@ -0,0 +1,492 @@ + /* + * SecY Operations + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * 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/defs.h" +#include "drivers/driver.h" +#include "pae/ieee802_1x_kay.h" +#include "pae/ieee802_1x_kay_i.h" +#include "pae/ieee802_1x_secy_ops.h" + + +int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay, + enum validate_frames vf) +{ + kay->vf = vf; + return 0; +} + + +int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->enable_protect_frames) { + wpa_printf(MSG_ERROR, + "KaY: secy enable_protect_frames operation not supported"); + return -1; + } + + return ops->enable_protect_frames(ops->ctx, enabled); +} + + +int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->set_replay_protect) { + wpa_printf(MSG_ERROR, + "KaY: secy set_replay_protect operation not supported"); + return -1; + } + + return ops->set_replay_protect(ops->ctx, enabled, win); +} + + +int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, + const u8 *cs, size_t cs_len) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->set_current_cipher_suite) { + wpa_printf(MSG_ERROR, + "KaY: secy set_current_cipher_suite operation not supported"); + return -1; + } + + return ops->set_current_cipher_suite(ops->ctx, cs, cs_len); +} + + +int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, + enum confidentiality_offset co) +{ + kay->co = co; + return 0; +} + + +int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->enable_controlled_port) { + wpa_printf(MSG_ERROR, + "KaY: secy enable_controlled_port operation not supported"); + return -1; + } + + return ops->enable_controlled_port(ops->ctx, enabled); +} + + +int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *rxsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->get_receive_lowest_pn) { + wpa_printf(MSG_ERROR, + "KaY: secy get_receive_lowest_pn operation not supported"); + return -1; + } + + return ops->get_receive_lowest_pn(ops->ctx, + rxsa->sc->channel, + rxsa->an, + &rxsa->lowest_pn); +} + + +int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->get_transmit_next_pn) { + wpa_printf(MSG_ERROR, + "KaY: secy get_receive_lowest_pn operation not supported"); + return -1; + } + + return ops->get_transmit_next_pn(ops->ctx, + txsa->sc->channel, + txsa->an, + &txsa->next_pn); +} + + +int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->set_transmit_next_pn) { + wpa_printf(MSG_ERROR, + "KaY: secy get_receive_lowest_pn operation not supported"); + return -1; + } + + return ops->set_transmit_next_pn(ops->ctx, + txsa->sc->channel, + txsa->an, + txsa->next_pn); +} + + +int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->get_available_receive_sc) { + wpa_printf(MSG_ERROR, + "KaY: secy get_available_receive_sc operation not supported"); + return -1; + } + + return ops->get_available_receive_sc(ops->ctx, channel); +} + + +int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsc) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->create_receive_sc) { + wpa_printf(MSG_ERROR, + "KaY: secy create_receive_sc operation not supported"); + return -1; + } + + return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci, + kay->vf, kay->co); +} + + +int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsc) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->delete_receive_sc) { + wpa_printf(MSG_ERROR, + "KaY: secy delete_receive_sc operation not supported"); + return -1; + } + + return ops->delete_receive_sc(ops->ctx, rxsc->channel); +} + + +int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->create_receive_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy create_receive_sa operation not supported"); + return -1; + } + + return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an, + rxsa->lowest_pn, rxsa->pkey->key); +} + + +int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->enable_receive_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy enable_receive_sa operation not supported"); + return -1; + } + + rxsa->enable_receive = TRUE; + + return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an); +} + + +int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !rxsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->disable_receive_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy disable_receive_sa operation not supported"); + return -1; + } + + rxsa->enable_receive = FALSE; + + return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an); +} + + +int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->get_available_transmit_sc) { + wpa_printf(MSG_ERROR, + "KaY: secy get_available_transmit_sc operation not supported"); + return -1; + } + + return ops->get_available_transmit_sc(ops->ctx, channel); +} + + +int secy_create_transmit_sc(struct ieee802_1x_kay *kay, + struct transmit_sc *txsc) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsc) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->create_transmit_sc) { + wpa_printf(MSG_ERROR, + "KaY: secy create_transmit_sc operation not supported"); + return -1; + } + + return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci, + kay->co); +} + + +int secy_delete_transmit_sc(struct ieee802_1x_kay *kay, + struct transmit_sc *txsc) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsc) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->delete_transmit_sc) { + wpa_printf(MSG_ERROR, + "KaY: secy delete_transmit_sc operation not supported"); + return -1; + } + + return ops->delete_transmit_sc(ops->ctx, txsc->channel); +} + + +int secy_create_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->create_transmit_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy create_transmit_sa operation not supported"); + return -1; + } + + return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an, + txsa->next_pn, txsa->confidentiality, + txsa->pkey->key); +} + + +int secy_enable_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->enable_transmit_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy enable_transmit_sa operation not supported"); + return -1; + } + + txsa->enable_transmit = TRUE; + + return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an); +} + + +int secy_disable_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->disable_transmit_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy disable_transmit_sa operation not supported"); + return -1; + } + + txsa->enable_transmit = FALSE; + + return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an); +} + + +int secy_init_macsec(struct ieee802_1x_kay *kay) +{ + int ret; + struct ieee802_1x_kay_ctx *ops; + struct macsec_init_params params; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->macsec_init) { + wpa_printf(MSG_ERROR, + "KaY: secy macsec_init operation not supported"); + return -1; + } + + params.use_es = FALSE; + params.use_scb = FALSE; + params.always_include_sci = TRUE; + + ret = ops->macsec_init(ops->ctx, ¶ms); + + return ret; +} + + +int secy_deinit_macsec(struct ieee802_1x_kay *kay) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->macsec_deinit) { + wpa_printf(MSG_ERROR, + "KaY: secy macsec_deinit operation not supported"); + return -1; + } + + return ops->macsec_deinit(ops->ctx); +} diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h new file mode 100644 index 000000000000..295b823a9d7f --- /dev/null +++ b/src/pae/ieee802_1x_secy_ops.h @@ -0,0 +1,62 @@ + /* + * SecY Operations + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_SECY_OPS_H +#define IEEE802_1X_SECY_OPS_H + +#include "common/defs.h" +#include "common/ieee802_1x_defs.h" + +struct ieee802_1x_kay_conf; +struct receive_sa; +struct transmit_sa; +struct receive_sc; +struct transmit_sc; + +int secy_init_macsec(struct ieee802_1x_kay *kay); +int secy_deinit_macsec(struct ieee802_1x_kay *kay); + +/****** CP -> SecY ******/ +int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay, + enum validate_frames vf); +int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag); +int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win); +int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, + const u8 *cs, size_t cs_len); +int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, + enum confidentiality_offset co); +int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag); + +/****** KaY -> SecY *******/ +int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *rxsa); +int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); +int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); +int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel); +int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); +int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); +int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); +int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); +int secy_disable_receive_sa(struct ieee802_1x_kay *kay, + struct receive_sa *rxsa); + +int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel); +int secy_create_transmit_sc(struct ieee802_1x_kay *kay, + struct transmit_sc *txsc); +int secy_delete_transmit_sc(struct ieee802_1x_kay *kay, + struct transmit_sc *txsc); +int secy_create_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); +int secy_enable_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); +int secy_disable_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); + +#endif /* IEEE802_1X_SECY_OPS_H */ diff --git a/src/radius/Makefile b/src/radius/Makefile index b199be8b1970..b5d063dac10d 100644 --- a/src/radius/Makefile +++ b/src/radius/Makefile @@ -1,7 +1,7 @@ all: libradius.a clean: - rm -f *~ *.o *.d libradius.a + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a install: @echo Nothing to be made. diff --git a/src/radius/radius.c b/src/radius/radius.c index d1feec96842f..8d878a4bd078 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen + * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -231,9 +231,32 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, - { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 } + { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_LOCATION_INFO, "Location-Information", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES, + "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES, + "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher", + RADIUS_ATTR_HEXDUMP }, }; -#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) +#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) static struct radius_attr_type *radius_get_attr_type(u8 type) @@ -249,25 +272,17 @@ static struct radius_attr_type *radius_get_attr_type(u8 type) } -static void print_char(char c) -{ - if (c >= 32 && c < 127) - printf("%c", c); - else - printf("<%02x>", c); -} - - static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) { struct radius_attr_type *attr; - int i, len; + int len; unsigned char *pos; + char buf[1000]; attr = radius_get_attr_type(hdr->type); - printf(" Attribute %d (%s) length=%d\n", - hdr->type, attr ? attr->name : "?Unknown?", hdr->length); + wpa_printf(MSG_INFO, " Attribute %d (%s) length=%d", + hdr->type, attr ? attr->name : "?Unknown?", hdr->length); if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) return; @@ -277,47 +292,50 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) switch (attr->data_type) { case RADIUS_ATTR_TEXT: - printf(" Value: '"); - for (i = 0; i < len; i++) - print_char(pos[i]); - printf("'\n"); + printf_encode(buf, sizeof(buf), pos, len); + wpa_printf(MSG_INFO, " Value: '%s'", buf); break; case RADIUS_ATTR_IP: if (len == 4) { struct in_addr addr; os_memcpy(&addr, pos, 4); - printf(" Value: %s\n", inet_ntoa(addr)); - } else - printf(" Invalid IP address length %d\n", len); + wpa_printf(MSG_INFO, " Value: %s", + inet_ntoa(addr)); + } else { + wpa_printf(MSG_INFO, " Invalid IP address length %d", + len); + } break; #ifdef CONFIG_IPV6 case RADIUS_ATTR_IPV6: if (len == 16) { - char buf[128]; const char *atxt; struct in6_addr *addr = (struct in6_addr *) pos; atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); - printf(" Value: %s\n", atxt ? atxt : "?"); - } else - printf(" Invalid IPv6 address length %d\n", len); + wpa_printf(MSG_INFO, " Value: %s", + atxt ? atxt : "?"); + } else { + wpa_printf(MSG_INFO, " Invalid IPv6 address length %d", + len); + } break; #endif /* CONFIG_IPV6 */ case RADIUS_ATTR_HEXDUMP: case RADIUS_ATTR_UNDIST: - printf(" Value:"); - for (i = 0; i < len; i++) - printf(" %02x", pos[i]); - printf("\n"); + wpa_snprintf_hex(buf, sizeof(buf), pos, len); + wpa_printf(MSG_INFO, " Value: %s", buf); break; case RADIUS_ATTR_INT32: if (len == 4) - printf(" Value: %u\n", WPA_GET_BE32(pos)); + wpa_printf(MSG_INFO, " Value: %u", + WPA_GET_BE32(pos)); else - printf(" Invalid INT32 length %d\n", len); + wpa_printf(MSG_INFO, " Invalid INT32 length %d", + len); break; default: @@ -330,9 +348,9 @@ void radius_msg_dump(struct radius_msg *msg) { size_t i; - printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", - msg->hdr->code, radius_code_string(msg->hdr->code), - msg->hdr->identifier, be_to_host16(msg->hdr->length)); + wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d", + msg->hdr->code, radius_code_string(msg->hdr->code), + msg->hdr->identifier, be_to_host16(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); @@ -384,7 +402,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { - printf("WARNING: Could not add Message-Authenticator\n"); + wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator"); return -1; } msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); @@ -473,6 +491,27 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, } +void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator) +{ + const u8 *addr[2]; + size_t len[2]; + + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); + os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN); + addr[0] = wpabuf_head(msg->buf); + len[0] = wpabuf_len(msg->buf); + addr[1] = secret; + len[1] = secret_len; + md5_vector(2, addr, len, msg->hdr->authenticator); + + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + } +} + + int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len) { @@ -491,7 +530,7 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); - return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; + return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; } @@ -518,7 +557,7 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); - if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) + if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) return 1; for (i = 0; i < msg->attr_used; i++) { @@ -551,7 +590,7 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, os_memcpy(msg->hdr->authenticator, orig_authenticator, sizeof(orig_authenticator)); - return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; + return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0; } @@ -585,7 +624,7 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, struct radius_attr_hdr *attr; if (data_len > RADIUS_MAX_ATTR_LEN) { - printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", + wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)", (unsigned long) data_len); return NULL; } @@ -756,8 +795,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, tmp = radius_get_attr_hdr(msg, i); if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { if (attr != NULL) { - printf("Multiple Message-Authenticator " - "attributes in RADIUS message\n"); + wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message"); return 1; } attr = tmp; @@ -765,7 +803,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, } if (attr == NULL) { - printf("No Message-Authenticator attribute found\n"); + wpa_printf(MSG_INFO, "No Message-Authenticator attribute found"); return 1; } @@ -785,8 +823,8 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, sizeof(orig_authenticator)); } - if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { - printf("Invalid Message-Authenticator!\n"); + if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "Invalid Message-Authenticator!"); return 1; } @@ -802,7 +840,7 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, u8 hash[MD5_MAC_LEN]; if (sent_msg == NULL) { - printf("No matching Access-Request message found\n"); + wpa_printf(MSG_INFO, "No matching Access-Request message found"); return 1; } @@ -822,8 +860,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); - if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { - printf("Response Authenticator invalid!\n"); + if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "Response Authenticator invalid!"); return 1; } @@ -918,7 +956,6 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, vhdr = (struct radius_attr_vendor *) pos; if (vhdr->vendor_length > left || vhdr->vendor_length < sizeof(*vhdr)) { - left = 0; break; } if (vhdr->vendor_type != subtype) { @@ -956,13 +993,17 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, /* key: 16-bit salt followed by encrypted key info */ - if (len < 2 + 16) + if (len < 2 + 16) { + wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d", + __func__, (int) len); return NULL; + } pos = key + 2; left = len - 2; if (left % 16) { - printf("Invalid ms key len %lu\n", (unsigned long) left); + wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu", + (unsigned long) left); return NULL; } @@ -996,7 +1037,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, } if (plain[0] == 0 || plain[0] > plen - 1) { - printf("Failed to decrypt MPPE key\n"); + wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key"); os_free(plain); return NULL; } @@ -1085,6 +1126,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, sent_msg->hdr->authenticator, secret, secret_len, &keys->send_len); + if (!keys->send) { + wpa_printf(MSG_DEBUG, + "RADIUS: Failed to decrypt send key"); + } os_free(key); } @@ -1096,6 +1141,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, sent_msg->hdr->authenticator, secret, secret_len, &keys->recv_len); + if (!keys->recv) { + wpa_printf(MSG_DEBUG, + "RADIUS: Failed to decrypt recv key"); + } os_free(key); } @@ -1204,30 +1253,55 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, } -/* Add User-Password attribute to a RADIUS message and encrypt it as specified - * in RFC 2865, Chap. 5.2 */ -struct radius_attr_hdr * -radius_msg_add_attr_user_password(struct radius_msg *msg, - const u8 *data, size_t data_len, - const u8 *secret, size_t secret_len) +int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, + size_t len) { - u8 buf[128]; - size_t padlen, i, buf_len, pos; + struct radius_attr_hdr *attr; + u8 *buf, *pos; + size_t alen; + + alen = 4 + 2 + len; + buf = os_malloc(alen); + if (buf == NULL) + return 0; + pos = buf; + WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA); + pos += 4; + *pos++ = subtype; + *pos++ = 2 + len; + os_memcpy(pos, data, len); + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, alen); + os_free(buf); + if (attr == NULL) + return 0; + + return 1; +} + + +int radius_user_password_hide(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len, + u8 *buf, size_t buf_len) +{ + size_t padlen, i, pos; const u8 *addr[2]; size_t len[2]; u8 hash[16]; - if (data_len > 128) - return NULL; + if (data_len + 16 > buf_len) + return -1; os_memcpy(buf, data, data_len); - buf_len = data_len; padlen = data_len % 16; - if (padlen && data_len < sizeof(buf)) { + if (padlen && data_len < buf_len) { padlen = 16 - padlen; os_memset(buf + data_len, 0, padlen); - buf_len += padlen; + buf_len = data_len + padlen; + } else { + buf_len = data_len; } addr[0] = secret; @@ -1253,8 +1327,27 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, pos += 16; } + return buf_len; +} + + +/* Add User-Password attribute to a RADIUS message and encrypt it as specified + * in RFC 2865, Chap. 5.2 */ +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len) +{ + u8 buf[128]; + int res; + + res = radius_user_password_hide(msg, data, data_len, + secret, secret_len, buf, sizeof(buf)); + if (res < 0) + return NULL; + return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, - buf, buf_len); + buf, res); } diff --git a/src/radius/radius.h b/src/radius/radius.h index 2031054b1d23..5977339e08d2 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, 2012, Jouni Malinen + * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -90,7 +90,21 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, - RADIUS_ATTR_ERROR_CAUSE = 101 + RADIUS_ATTR_ERROR_CAUSE = 101, + RADIUS_ATTR_EAP_KEY_NAME = 102, + RADIUS_ATTR_OPERATOR_NAME = 126, + RADIUS_ATTR_LOCATION_INFO = 127, + RADIUS_ATTR_LOCATION_DATA = 128, + RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES = 129, + RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130, + RADIUS_ATTR_LOCATION_CAPABLE = 131, + RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132, + RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177, + RADIUS_ATTR_WLAN_HESSID = 181, + RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186, + RADIUS_ATTR_WLAN_GROUP_CIPHER = 187, + RADIUS_ATTR_WLAN_AKM_SUITE = 188, + RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189, }; @@ -163,6 +177,18 @@ enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 }; + +/* Hotspot 2.0 - WFA Vendor-specific RADIUS Attributes */ +#define RADIUS_VENDOR_ID_WFA 40808 + +enum { + RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1, + RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2, + RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3, + RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4, + RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5, +}; + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -204,6 +230,9 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, const struct radius_hdr *req_hdr); void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len); +void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const u8 *req_authenticator); int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, @@ -234,6 +263,12 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *send_key, size_t send_key_len, const u8 *recv_key, size_t recv_key_len); +int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data, + size_t len); +int radius_user_password_hide(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len, + u8 *buf, size_t buf_len); struct radius_attr_hdr * radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *data, size_t data_len, diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 425ad935afbf..693f61ea0455 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -1,6 +1,6 @@ /* * RADIUS client - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -122,7 +122,7 @@ struct radius_msg_list { /** * last_attempt - Time of the last transmission attempt */ - struct os_time last_attempt; + struct os_reltime last_attempt; /** * shared_secret - Shared secret with the target RADIUS server @@ -236,6 +236,8 @@ radius_change_server(struct radius_client_data *radius, int sock, int sock6, int auth); static int radius_client_init_acct(struct radius_client_data *radius); static int radius_client_init_auth(struct radius_client_data *radius); +static void radius_client_auth_failover(struct radius_client_data *radius); +static void radius_client_acct_failover(struct radius_client_data *radius); static void radius_client_msg_free(struct radius_msg_list *req) @@ -295,26 +297,34 @@ int radius_client_register(struct radius_client_data *radius, } -static void radius_client_handle_send_error(struct radius_client_data *radius, - int s, RadiusType msg_type) +/* + * Returns >0 if message queue was flushed (i.e., the message that triggered + * the error is not available anymore) + */ +static int radius_client_handle_send_error(struct radius_client_data *radius, + int s, RadiusType msg_type) { #ifndef CONFIG_NATIVE_WINDOWS int _errno = errno; - perror("send[RADIUS]"); + wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || - _errno == EBADF) { + _errno == EBADF || _errno == ENETUNREACH) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" " try to connect again"); - eloop_unregister_read_sock(s); - close(s); - if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) + if (msg_type == RADIUS_ACCT || + msg_type == RADIUS_ACCT_INTERIM) { radius_client_init_acct(radius); - else + return 0; + } else { radius_client_init_auth(radius); + return 1; + } } #endif /* CONFIG_NATIVE_WINDOWS */ + + return 0; } @@ -325,9 +335,18 @@ static int radius_client_retransmit(struct radius_client_data *radius, struct hostapd_radius_servers *conf = radius->conf; int s; struct wpabuf *buf; + size_t prev_num_msgs; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { + if (radius->acct_sock < 0) + radius_client_init_acct(radius); + if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { + prev_num_msgs = radius->num_msgs; + radius_client_acct_failover(radius); + if (prev_num_msgs != radius->num_msgs) + return 0; + } s = radius->acct_sock; if (entry->attempts == 0) conf->acct_server->requests++; @@ -336,6 +355,14 @@ static int radius_client_retransmit(struct radius_client_data *radius, conf->acct_server->retransmissions++; } } else { + if (radius->auth_sock < 0) + radius_client_init_auth(radius); + if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { + prev_num_msgs = radius->num_msgs; + radius_client_auth_failover(radius); + if (prev_num_msgs != radius->num_msgs) + return 0; + } s = radius->auth_sock; if (entry->attempts == 0) conf->auth_server->requests++; @@ -344,6 +371,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, conf->auth_server->retransmissions++; } } + if (s < 0) { + wpa_printf(MSG_INFO, + "RADIUS: No valid socket for retransmission"); + return 1; + } /* retransmit; remove entry if too many attempts */ entry->attempts++; @@ -351,18 +383,20 @@ static int radius_client_retransmit(struct radius_client_data *radius, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); - os_get_time(&entry->last_attempt); + os_get_reltime(&entry->last_attempt); buf = radius_msg_get_buf(entry->msg); - if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) - radius_client_handle_send_error(radius, s, entry->msg_type); + if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + if (radius_client_handle_send_error(radius, s, entry->msg_type) + > 0) + return 0; + } entry->next_try = now + entry->next_wait; entry->next_wait *= 2; if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) entry->next_wait = RADIUS_CLIENT_MAX_WAIT; if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { - printf("Removing un-ACKed RADIUS message due to too many " - "failed retransmit attempts\n"); + wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } @@ -374,21 +408,23 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; - struct os_time now; + struct os_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; - char abuf[50]; + size_t prev_num_msgs; + int s; entry = radius->msgs; if (!entry) return; - os_get_time(&now); + os_get_reltime(&now); first = 0; prev = NULL; while (entry) { + prev_num_msgs = radius->num_msgs; if (now.sec >= entry->next_try && radius_client_retransmit(radius, entry, now.sec)) { if (prev) @@ -403,7 +439,18 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) continue; } - if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { + if (prev_num_msgs != radius->num_msgs) { + wpa_printf(MSG_DEBUG, + "RADIUS: Message removed from queue - restart from beginning"); + entry = radius->msgs; + prev = NULL; + continue; + } + + s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock : + radius->acct_sock; + if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER || + (s < 0 && entry->attempts > 0)) { if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) acct_failover++; @@ -429,60 +476,76 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) (long int) (first - now.sec)); } - if (auth_failover && conf->num_auth_servers > 1) { - struct hostapd_radius_server *next, *old; - old = conf->auth_server; - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_NOTICE, - "No response from Authentication server " - "%s:%d - failover", - hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), - old->port); - - for (entry = radius->msgs; entry; entry = entry->next) { - if (entry->msg_type == RADIUS_AUTH) - old->timeouts++; - } + if (auth_failover && conf->num_auth_servers > 1) + radius_client_auth_failover(radius); + + if (acct_failover && conf->num_acct_servers > 1) + radius_client_acct_failover(radius); +} - next = old + 1; - if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) - next = conf->auth_servers; - conf->auth_server = next; - radius_change_server(radius, next, old, - radius->auth_serv_sock, - radius->auth_serv_sock6, 1); + +static void radius_client_auth_failover(struct radius_client_data *radius) +{ + struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_server *next, *old; + struct radius_msg_list *entry; + char abuf[50]; + + old = conf->auth_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Authentication server %s:%d - failover", + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_AUTH) + old->timeouts++; } - if (acct_failover && conf->num_acct_servers > 1) { - struct hostapd_radius_server *next, *old; - old = conf->acct_server; - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_NOTICE, - "No response from Accounting server " - "%s:%d - failover", - hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), - old->port); + next = old + 1; + if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) + next = conf->auth_servers; + conf->auth_server = next; + radius_change_server(radius, next, old, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); +} - for (entry = radius->msgs; entry; entry = entry->next) { - if (entry->msg_type == RADIUS_ACCT || - entry->msg_type == RADIUS_ACCT_INTERIM) - old->timeouts++; - } - next = old + 1; - if (next > &conf->acct_servers[conf->num_acct_servers - 1]) - next = conf->acct_servers; - conf->acct_server = next; - radius_change_server(radius, next, old, - radius->acct_serv_sock, - radius->acct_serv_sock6, 0); +static void radius_client_acct_failover(struct radius_client_data *radius) +{ + struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_server *next, *old; + struct radius_msg_list *entry; + char abuf[50]; + + old = conf->acct_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Accounting server %s:%d - failover", + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + old->timeouts++; } + + next = old + 1; + if (next > &conf->acct_servers[conf->num_acct_servers - 1]) + next = conf->acct_servers; + conf->acct_server = next; + radius_change_server(radius, next, old, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); } static void radius_client_update_timeout(struct radius_client_data *radius) { - struct os_time now; + struct os_reltime now; os_time_t first; struct radius_msg_list *entry; @@ -498,7 +561,7 @@ static void radius_client_update_timeout(struct radius_client_data *radius) first = entry->next_try; } - os_get_time(&now); + os_get_reltime(&now); if (first < now.sec) first = now.sec; eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, @@ -526,7 +589,7 @@ static void radius_client_list_add(struct radius_client_data *radius, entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { - printf("Failed to add RADIUS packet into retransmit list\n"); + wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); radius_msg_free(msg); return; } @@ -537,7 +600,7 @@ static void radius_client_list_add(struct radius_client_data *radius, entry->msg_type = msg_type; entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; - os_get_time(&entry->last_attempt); + os_get_reltime(&entry->last_attempt); entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; @@ -547,8 +610,7 @@ static void radius_client_list_add(struct radius_client_data *radius, radius_client_update_timeout(radius); if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { - printf("Removing the oldest un-ACKed RADIUS packet due to " - "retransmit list limits.\n"); + wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); prev = NULL; while (entry->next) { prev = entry; @@ -635,7 +697,11 @@ int radius_client_send(struct radius_client_data *radius, } if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { - if (conf->acct_server == NULL) { + if (conf->acct_server && radius->acct_sock < 0) + radius_client_init_acct(radius); + + if (conf->acct_server == NULL || radius->acct_sock < 0 || + conf->acct_server->shared_secret == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -649,7 +715,11 @@ int radius_client_send(struct radius_client_data *radius, s = radius->acct_sock; conf->acct_server->requests++; } else { - if (conf->auth_server == NULL) { + if (conf->auth_server && radius->auth_sock < 0) + radius_client_init_auth(radius); + + if (conf->auth_server == NULL || radius->auth_sock < 0 || + conf->auth_server->shared_secret == NULL) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -694,7 +764,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) struct radius_rx_handler *handlers; size_t num_handlers, i; struct radius_msg_list *req, *prev_req; - struct os_time now; + struct os_reltime now; struct hostapd_radius_server *rconf; int invalid_authenticator = 0; @@ -710,21 +780,20 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { - perror("recv[RADIUS]"); + wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno)); return; } hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " "server", len); if (len == sizeof(buf)) { - printf("Possibly too long UDP frame for our buffer - " - "dropping it\n"); + wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); return; } msg = radius_msg_parse(buf, len); if (msg == NULL) { - printf("Parsing incoming RADIUS frame failed\n"); + wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); rconf->malformed_responses++; return; } @@ -775,7 +844,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) goto fail; } - os_get_time(&now); + os_get_reltime(&now); roundtrip = (now.sec - req->last_attempt.sec) * 100 + (now.usec - req->last_attempt.usec) / 10000; hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, @@ -954,9 +1023,10 @@ radius_change_server(struct radius_client_data *radius, hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), nserv->port); - if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || - os_memcmp(nserv->shared_secret, oserv->shared_secret, - nserv->shared_secret_len) != 0) { + if (oserv && oserv != nserv && + (nserv->shared_secret_len != oserv->shared_secret_len || + os_memcmp(nserv->shared_secret, oserv->shared_secret, + nserv->shared_secret_len) != 0)) { /* Pending RADIUS packets used different shared secret, so * they need to be modified. Update accounting message * authenticators here. Authentication messages are removed @@ -974,7 +1044,8 @@ radius_change_server(struct radius_client_data *radius, } /* Reset retry counters for the new server */ - for (entry = radius->msgs; entry; entry = entry->next) { + for (entry = radius->msgs; oserv && oserv != nserv && entry; + entry = entry->next) { if ((auth && entry->msg_type != RADIUS_AUTH) || (!auth && entry->msg_type != RADIUS_ACCT)) continue; @@ -1015,6 +1086,13 @@ radius_change_server(struct radius_client_data *radius, return -1; } + if (sel_sock < 0) { + wpa_printf(MSG_INFO, + "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d", + nserv->addr.af, sock, sock6, auth); + return -1; + } + if (conf->force_client_addr) { switch (conf->client_addr.af) { case AF_INET: @@ -1041,13 +1119,14 @@ radius_change_server(struct radius_client_data *radius, } if (bind(sel_sock, cl_addr, claddrlen) < 0) { - perror("bind[radius]"); + wpa_printf(MSG_INFO, "bind[radius]: %s", + strerror(errno)); return -1; } } if (connect(sel_sock, addr, addrlen) < 0) { - perror("connect[radius]"); + wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; } @@ -1055,19 +1134,23 @@ radius_change_server(struct radius_client_data *radius, switch (nserv->addr.af) { case AF_INET: claddrlen = sizeof(claddr); - getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); - wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", - inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); + if (getsockname(sel_sock, (struct sockaddr *) &claddr, + &claddrlen) == 0) { + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntoa(claddr.sin_addr), + ntohs(claddr.sin_port)); + } break; #ifdef CONFIG_IPV6 case AF_INET6: { claddrlen = sizeof(claddr6); - getsockname(sel_sock, (struct sockaddr *) &claddr6, - &claddrlen); - wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", - inet_ntop(AF_INET6, &claddr6.sin6_addr, - abuf, sizeof(abuf)), - ntohs(claddr6.sin6_port)); + if (getsockname(sel_sock, (struct sockaddr *) &claddr6, + &claddrlen) == 0) { + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntop(AF_INET6, &claddr6.sin6_addr, + abuf, sizeof(abuf)), + ntohs(claddr6.sin6_port)); + } break; } #endif /* CONFIG_IPV6 */ @@ -1093,18 +1176,28 @@ static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) conf->auth_server != conf->auth_servers) { oserv = conf->auth_server; conf->auth_server = conf->auth_servers; - radius_change_server(radius, conf->auth_server, oserv, - radius->auth_serv_sock, - radius->auth_serv_sock6, 1); + if (radius_change_server(radius, conf->auth_server, oserv, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1) < 0) { + conf->auth_server = oserv; + radius_change_server(radius, oserv, conf->auth_server, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); + } } if (radius->acct_sock >= 0 && conf->acct_servers && conf->acct_server != conf->acct_servers) { oserv = conf->acct_server; conf->acct_server = conf->acct_servers; - radius_change_server(radius, conf->acct_server, oserv, - radius->acct_serv_sock, - radius->acct_serv_sock6, 0); + if (radius_change_server(radius, conf->acct_server, oserv, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0) < 0) { + conf->acct_server = oserv; + radius_change_server(radius, oserv, conf->acct_server, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); + } } if (conf->retry_primary_interval) @@ -1123,21 +1216,62 @@ static int radius_client_disable_pmtu_discovery(int s) r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, sizeof(action)); if (r == -1) - wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " - "%s", strerror(errno)); + wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", + strerror(errno)); #endif return r; } +static void radius_close_auth_sockets(struct radius_client_data *radius) +{ + radius->auth_sock = -1; + + if (radius->auth_serv_sock >= 0) { + eloop_unregister_read_sock(radius->auth_serv_sock); + close(radius->auth_serv_sock); + radius->auth_serv_sock = -1; + } +#ifdef CONFIG_IPV6 + if (radius->auth_serv_sock6 >= 0) { + eloop_unregister_read_sock(radius->auth_serv_sock6); + close(radius->auth_serv_sock6); + radius->auth_serv_sock6 = -1; + } +#endif /* CONFIG_IPV6 */ +} + + +static void radius_close_acct_sockets(struct radius_client_data *radius) +{ + radius->acct_sock = -1; + + if (radius->acct_serv_sock >= 0) { + eloop_unregister_read_sock(radius->acct_serv_sock); + close(radius->acct_serv_sock); + radius->acct_serv_sock = -1; + } +#ifdef CONFIG_IPV6 + if (radius->acct_serv_sock6 >= 0) { + eloop_unregister_read_sock(radius->acct_serv_sock6); + close(radius->acct_serv_sock6); + radius->acct_serv_sock6 = -1; + } +#endif /* CONFIG_IPV6 */ +} + + static int radius_client_init_auth(struct radius_client_data *radius) { struct hostapd_radius_servers *conf = radius->conf; int ok = 0; + radius_close_auth_sockets(radius); + radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->auth_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->auth_serv_sock); ok++; @@ -1146,7 +1280,8 @@ static int radius_client_init_auth(struct radius_client_data *radius) #ifdef CONFIG_IPV6 radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); if (radius->auth_serv_sock6 < 0) - perror("socket[PF_INET6,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ @@ -1162,8 +1297,8 @@ static int radius_client_init_auth(struct radius_client_data *radius) eloop_register_read_sock(radius->auth_serv_sock, radius_client_receive, radius, (void *) RADIUS_AUTH)) { - printf("Could not register read socket for authentication " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); + radius_close_auth_sockets(radius); return -1; } @@ -1172,8 +1307,8 @@ static int radius_client_init_auth(struct radius_client_data *radius) eloop_register_read_sock(radius->auth_serv_sock6, radius_client_receive, radius, (void *) RADIUS_AUTH)) { - printf("Could not register read socket for authentication " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); + radius_close_auth_sockets(radius); return -1; } #endif /* CONFIG_IPV6 */ @@ -1187,9 +1322,12 @@ static int radius_client_init_acct(struct radius_client_data *radius) struct hostapd_radius_servers *conf = radius->conf; int ok = 0; + radius_close_acct_sockets(radius); + radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->acct_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->acct_serv_sock); ok++; @@ -1198,7 +1336,8 @@ static int radius_client_init_acct(struct radius_client_data *radius) #ifdef CONFIG_IPV6 radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); if (radius->acct_serv_sock6 < 0) - perror("socket[PF_INET6,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ @@ -1214,8 +1353,8 @@ static int radius_client_init_acct(struct radius_client_data *radius) eloop_register_read_sock(radius->acct_serv_sock, radius_client_receive, radius, (void *) RADIUS_ACCT)) { - printf("Could not register read socket for accounting " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); + radius_close_acct_sockets(radius); return -1; } @@ -1224,8 +1363,8 @@ static int radius_client_init_acct(struct radius_client_data *radius) eloop_register_read_sock(radius->acct_serv_sock6, radius_client_receive, radius, (void *) RADIUS_ACCT)) { - printf("Could not register read socket for accounting " - "server\n"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); + radius_close_acct_sockets(radius); return -1; } #endif /* CONFIG_IPV6 */ @@ -1287,16 +1426,8 @@ void radius_client_deinit(struct radius_client_data *radius) if (!radius) return; - if (radius->auth_serv_sock >= 0) - eloop_unregister_read_sock(radius->auth_serv_sock); - if (radius->acct_serv_sock >= 0) - eloop_unregister_read_sock(radius->acct_serv_sock); -#ifdef CONFIG_IPV6 - if (radius->auth_serv_sock6 >= 0) - eloop_unregister_read_sock(radius->auth_serv_sock6); - if (radius->acct_serv_sock6 >= 0) - eloop_unregister_read_sock(radius->acct_serv_sock6); -#endif /* CONFIG_IPV6 */ + radius_close_auth_sockets(radius); + radius_close_acct_sockets(radius); eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c index bded96519929..39ceea879caf 100644 --- a/src/radius/radius_das.c +++ b/src/radius/radius_das.c @@ -1,6 +1,6 @@ /* * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) - * Copyright (c) 2012, Jouni Malinen + * Copyright (c) 2012-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -16,9 +16,6 @@ #include "radius_das.h" -extern int wpa_debug_level; - - struct radius_das_data { int sock; u8 *shared_secret; @@ -41,11 +38,17 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, struct radius_msg *reply; u8 allowed[] = { RADIUS_ATTR_USER_NAME, + RADIUS_ATTR_NAS_IP_ADDRESS, RADIUS_ATTR_CALLING_STATION_ID, + RADIUS_ATTR_NAS_IDENTIFIER, RADIUS_ATTR_ACCT_SESSION_ID, + RADIUS_ATTR_ACCT_MULTI_SESSION_ID, RADIUS_ATTR_EVENT_TIMESTAMP, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, +#ifdef CONFIG_IPV6 + RADIUS_ATTR_NAS_IPV6_ADDRESS, +#endif /* CONFIG_IPV6 */ 0 }; int error = 405; @@ -70,6 +73,36 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, os_memset(&attrs, 0, sizeof(attrs)); + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + &buf, &len, NULL) == 0) { + if (len != 4) { + wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d", + abuf, from_port); + error = 407; + goto fail; + } + attrs.nas_ip_addr = buf; + } + +#ifdef CONFIG_IPV6 + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + &buf, &len, NULL) == 0) { + if (len != 16) { + wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d", + abuf, from_port); + error = 407; + goto fail; + } + attrs.nas_ipv6_addr = buf; + } +#endif /* CONFIG_IPV6 */ + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + &buf, &len, NULL) == 0) { + attrs.nas_identifier = buf; + attrs.nas_identifier_len = len; + } + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &buf, &len, NULL) == 0) { if (len >= sizeof(tmp)) @@ -97,6 +130,12 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, attrs.acct_session_id_len = len; } + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_multi_session_id = buf; + attrs.acct_multi_session_id_len = len; + } + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, &buf, &len, NULL) == 0) { attrs.cui = buf; @@ -115,6 +154,12 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, "%s:%d", abuf, from_port); error = 503; break; + case RADIUS_DAS_MULTI_SESSION_MATCH: + wpa_printf(MSG_INFO, + "DAS: Multiple sessions match for request from %s:%d", + abuf, from_port); + error = 508; + break; case RADIUS_DAS_SUCCESS: error = 0; break; @@ -200,7 +245,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) (u8 *) &val, 4); if (res == 4) { u32 timestamp = ntohl(val); - if (abs(now.sec - timestamp) > das->time_window) { + if ((unsigned int) abs(now.sec - timestamp) > + das->time_window) { wpa_printf(MSG_DEBUG, "DAS: Unacceptable " "Event-Timestamp (%u; local time %u) in " "packet from %s:%d - drop", @@ -284,7 +330,7 @@ static int radius_das_open_socket(int port) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno)); return -1; } @@ -292,7 +338,7 @@ static int radius_das_open_socket(int port) addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno)); close(s); return -1; } diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h index 738b18b059d6..ce731d46a9ac 100644 --- a/src/radius/radius_das.h +++ b/src/radius/radius_das.h @@ -14,15 +14,25 @@ struct radius_das_data; enum radius_das_res { RADIUS_DAS_SUCCESS, RADIUS_DAS_NAS_MISMATCH, - RADIUS_DAS_SESSION_NOT_FOUND + RADIUS_DAS_SESSION_NOT_FOUND, + RADIUS_DAS_MULTI_SESSION_MATCH, }; struct radius_das_attrs { + /* NAS identification attributes */ + const u8 *nas_ip_addr; + const u8 *nas_identifier; + size_t nas_identifier_len; + const u8 *nas_ipv6_addr; + + /* Session identification attributes */ const u8 *sta_addr; const u8 *user_name; size_t user_name_len; const u8 *acct_session_id; size_t acct_session_id_len; + const u8 *acct_multi_session_id; + size_t acct_multi_session_id_len; const u8 *cui; size_t cui_len; }; diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 5b2d71111810..85a485e91d93 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -1,6 +1,6 @@ /* * RADIUS authentication server - * Copyright (c) 2005-2009, 2011, Jouni Malinen + * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -8,11 +8,16 @@ #include "includes.h" #include +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "common.h" #include "radius.h" #include "eloop.h" #include "eap_server/eap.h" +#include "ap/ap_config.h" +#include "crypto/tls.h" #include "radius_server.h" /** @@ -49,6 +54,13 @@ struct radius_server_counters { u32 bad_authenticators; u32 packets_dropped; u32 unknown_types; + + u32 acct_requests; + u32 invalid_acct_requests; + u32 acct_responses; + u32 malformed_acct_requests; + u32 acct_bad_authenticators; + u32 unknown_acct_types; }; /** @@ -61,6 +73,8 @@ struct radius_session { unsigned int sess_id; struct eap_sm *eap; struct eap_eapol_interface *eap_if; + char *username; /* from User-Name attribute */ + char *nas_ip; struct radius_msg *last_msg; char *last_from_addr; @@ -70,6 +84,11 @@ struct radius_session { u8 last_identifier; struct radius_msg *last_reply; u8 last_authenticator[16]; + + unsigned int remediation:1; + unsigned int macacl:1; + + struct hostapd_radius_attr *accept_attr; }; /** @@ -98,6 +117,11 @@ struct radius_server_data { */ int auth_sock; + /** + * acct_sock - Socket for RADIUS accounting messages + */ + int acct_sock; + /** * clients - List of authorized RADIUS clients */ @@ -222,6 +246,25 @@ struct radius_server_data { */ u16 pwd_group; + /** + * server_id - Server identity + */ + const char *server_id; + + /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + * + * This controls whether the authentication server derives ERP key + * hierarchy (rRK and rIK) from full EAP authentication and allows + * these keys to be used to perform ERP to derive rMSK instead of full + * EAP authentication to derive MSK. + */ + int erp; + + const char *erp_domain; + + struct dl_list erp_keys; /* struct eap_server_erp_key */ + /** * wps - Wi-Fi Protected Setup context * @@ -239,7 +282,7 @@ struct radius_server_data { /** * start_time - Timestamp of server start */ - struct os_time start_time; + struct os_reltime start_time; /** * counters - Statistics counters for server operations @@ -290,10 +333,15 @@ struct radius_server_data { #ifdef CONFIG_RADIUS_TEST char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ -}; + char *subscr_remediation_url; + u8 subscr_remediation_method; + +#ifdef CONFIG_SQLITE + sqlite3 *db; +#endif /* CONFIG_SQLITE */ +}; -extern int wpa_debug_level; #define RADIUS_DEBUG(args...) \ wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) @@ -309,6 +357,52 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); static void radius_server_session_remove_timeout(void *eloop_ctx, void *timeout_ctx); +void srv_log(struct radius_session *sess, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + +void srv_log(struct radius_session *sess, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf); + +#ifdef CONFIG_SQLITE + if (sess->server->db) { + char *sql; + sql = sqlite3_mprintf("INSERT INTO authlog" + "(timestamp,session,nas_ip,username,note)" + " VALUES (" + "strftime('%%Y-%%m-%%d %%H:%%M:%%f'," + "'now'),%u,%Q,%Q,%Q)", + sess->sess_id, sess->nas_ip, + sess->username, buf); + if (sql) { + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, + NULL) != SQLITE_OK) { + RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); + } + } +#endif /* CONFIG_SQLITE */ + + os_free(buf); +} + static struct radius_client * radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, @@ -374,6 +468,8 @@ static void radius_server_session_free(struct radius_server_data *data, radius_msg_free(sess->last_msg); os_free(sess->last_from_addr); radius_msg_free(sess->last_reply); + os_free(sess->username); + os_free(sess->nas_ip); os_free(sess); data->num_sess--; } @@ -453,47 +549,125 @@ radius_server_new_session(struct radius_server_data *data, } +#ifdef CONFIG_TESTING_OPTIONS +static void radius_server_testing_options_tls(struct radius_session *sess, + const char *tls, + struct eap_config *eap_conf) +{ + int test = atoi(tls); + + switch (test) { + case 1: + srv_log(sess, "TLS test - break VerifyData"); + eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA; + break; + case 2: + srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash"); + eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH; + break; + case 3: + srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature"); + eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE; + break; + case 4: + srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime"); + eap_conf->tls_test_flags = TLS_DHE_PRIME_511B; + break; + case 5: + srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime"); + eap_conf->tls_test_flags = TLS_DHE_PRIME_767B; + break; + case 6: + srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\""); + eap_conf->tls_test_flags = TLS_DHE_PRIME_15; + break; + case 7: + srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container"); + eap_conf->tls_test_flags = TLS_DHE_PRIME_58B; + break; + case 8: + srv_log(sess, "TLS test - RSA-DHE using a non-prime"); + eap_conf->tls_test_flags = TLS_DHE_NON_PRIME; + break; + default: + srv_log(sess, "Unrecognized TLS test"); + break; + } +} +#endif /* CONFIG_TESTING_OPTIONS */ + +static void radius_server_testing_options(struct radius_session *sess, + struct eap_config *eap_conf) +{ +#ifdef CONFIG_TESTING_OPTIONS + const char *pos; + + pos = os_strstr(sess->username, "@test-"); + if (pos == NULL) + return; + pos += 6; + if (os_strncmp(pos, "tls-", 4) == 0) + radius_server_testing_options_tls(sess, pos + 4, eap_conf); + else + srv_log(sess, "Unrecognized test: %s", pos); +#endif /* CONFIG_TESTING_OPTIONS */ +} + + static struct radius_session * radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, - struct radius_msg *msg) + struct radius_msg *msg, const char *from_addr) { u8 *user; size_t user_len; int res; struct radius_session *sess; struct eap_config eap_conf; + struct eap_user tmp; RADIUS_DEBUG("Creating a new session"); - user = os_malloc(256); - if (user == NULL) { - return NULL; - } - res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); - if (res < 0 || res > 256) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user, + &user_len, NULL) < 0) { RADIUS_DEBUG("Could not get User-Name"); - os_free(user); return NULL; } - user_len = res; RADIUS_DUMP_ASCII("User-Name", user, user_len); - res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); - os_free(user); + os_memset(&tmp, 0, sizeof(tmp)); + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp); + bin_clear_free(tmp.password, tmp.password_len); - if (res == 0) { - RADIUS_DEBUG("Matching user entry found"); - sess = radius_server_new_session(data, client); - if (sess == NULL) { - RADIUS_DEBUG("Failed to create a new session"); - return NULL; - } - } else { + if (res != 0) { RADIUS_DEBUG("User-Name not found from user database"); return NULL; } + RADIUS_DEBUG("Matching user entry found"); + sess = radius_server_new_session(data, client); + if (sess == NULL) { + RADIUS_DEBUG("Failed to create a new session"); + return NULL; + } + sess->accept_attr = tmp.accept_attr; + sess->macacl = tmp.macacl; + + sess->username = os_malloc(user_len * 4 + 1); + if (sess->username == NULL) { + radius_server_session_free(data, sess); + return NULL; + } + printf_encode(sess->username, user_len * 4 + 1, user, user_len); + + sess->nas_ip = os_strdup(from_addr); + if (sess->nas_ip == NULL) { + radius_server_session_free(data, sess); + return NULL; + } + + srv_log(sess, "New session created"); + os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = data->ssl_ctx; eap_conf.msg_ctx = data->msg_ctx; @@ -511,6 +685,10 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; eap_conf.pwd_group = data->pwd_group; + eap_conf.server_id = (const u8 *) data->server_id; + eap_conf.server_id_len = os_strlen(data->server_id); + eap_conf.erp = data->erp; + radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -605,12 +783,134 @@ radius_server_encapsulate_eap(struct radius_server_data *data, } } +#ifdef CONFIG_HS20 + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation && + data->subscr_remediation_url) { + u8 *buf; + size_t url_len = os_strlen(data->subscr_remediation_url); + buf = os_malloc(1 + url_len); + if (buf == NULL) { + radius_msg_free(msg); + return NULL; + } + buf[0] = data->subscr_remediation_method; + os_memcpy(&buf[1], data->subscr_remediation_url, url_len); + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, + buf, 1 + url_len)) { + RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); + } + os_free(buf); + } else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) { + u8 buf[1]; + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION, + buf, 0)) { + RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); + } + } +#endif /* CONFIG_HS20 */ + if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); return NULL; } + if (code == RADIUS_CODE_ACCESS_ACCEPT) { + struct hostapd_radius_attr *attr; + for (attr = sess->accept_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS attribute"); + radius_msg_free(msg); + return NULL; + } + } + } + + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, + client->shared_secret_len, + hdr->authenticator) < 0) { + RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + } + + return msg; +} + + +static struct radius_msg * +radius_server_macacl(struct radius_server_data *data, + struct radius_client *client, + struct radius_session *sess, + struct radius_msg *request) +{ + struct radius_msg *msg; + int code; + struct radius_hdr *hdr = radius_msg_get_hdr(request); + u8 *pw; + size_t pw_len; + + code = RADIUS_CODE_ACCESS_ACCEPT; + + if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw, + &pw_len, NULL) < 0) { + RADIUS_DEBUG("Could not get User-Password"); + code = RADIUS_CODE_ACCESS_REJECT; + } else { + int res; + struct eap_user tmp; + + os_memset(&tmp, 0, sizeof(tmp)); + res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username, + os_strlen(sess->username), 0, &tmp); + if (res || !tmp.macacl || tmp.password == NULL) { + RADIUS_DEBUG("No MAC ACL user entry"); + bin_clear_free(tmp.password, tmp.password_len); + code = RADIUS_CODE_ACCESS_REJECT; + } else { + u8 buf[128]; + res = radius_user_password_hide( + request, tmp.password, tmp.password_len, + (u8 *) client->shared_secret, + client->shared_secret_len, + buf, sizeof(buf)); + bin_clear_free(tmp.password, tmp.password_len); + + if (res < 0 || pw_len != (size_t) res || + os_memcmp_const(pw, buf, res) != 0) { + RADIUS_DEBUG("Incorrect User-Password"); + code = RADIUS_CODE_ACCESS_REJECT; + } + } + } + + msg = radius_msg_new(code, hdr->identifier); + if (msg == NULL) { + RADIUS_DEBUG("Failed to allocate reply message"); + return NULL; + } + + if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { + RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); + radius_msg_free(msg); + return NULL; + } + + if (code == RADIUS_CODE_ACCESS_ACCEPT) { + struct hostapd_radius_attr *attr; + for (attr = sess->accept_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS attribute"); + radius_msg_free(msg); + return NULL; + } + } + } + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, hdr->authenticator) < 0) { @@ -672,7 +972,7 @@ static int radius_server_reject(struct radius_server_data *data, buf = radius_msg_get_buf(msg); if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, (struct sockaddr *) from, sizeof(*from)) < 0) { - perror("sendto[RADIUS SRV]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); ret = -1; } @@ -719,7 +1019,8 @@ static int radius_server_request(struct radius_server_data *data, from_addr, from_port); return -1; } else { - sess = radius_server_get_new_session(data, client, msg); + sess = radius_server_get_new_session(data, client, msg, + from_addr); if (sess == NULL) { RADIUS_DEBUG("Could not create a new session"); radius_server_reject(data, client, msg, from, fromlen, @@ -743,7 +1044,8 @@ static int radius_server_request(struct radius_server_data *data, wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { - perror("sendto[RADIUS SRV]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); } return 0; } @@ -754,6 +1056,12 @@ static int radius_server_request(struct radius_server_data *data, } eap = radius_msg_get_eap(msg); + if (eap == NULL && sess->macacl) { + reply = radius_server_macacl(data, client, sess, msg); + if (reply == NULL) + return -1; + goto send_reply; + } if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); @@ -804,9 +1112,14 @@ static int radius_server_request(struct radius_server_data *data, if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) is_complete = 1; + if (sess->eap_if->eapFail) + srv_log(sess, "EAP authentication failed"); + else if (sess->eap_if->eapSuccess) + srv_log(sess, "EAP authentication succeeded"); reply = radius_server_encapsulate_eap(data, client, sess, msg); +send_reply: if (reply) { struct wpabuf *buf; struct radius_hdr *hdr; @@ -818,10 +1131,12 @@ static int radius_server_request(struct radius_server_data *data, switch (radius_msg_get_hdr(reply)->code) { case RADIUS_CODE_ACCESS_ACCEPT: + srv_log(sess, "Sending Access-Accept"); data->counters.access_accepts++; client->counters.access_accepts++; break; case RADIUS_CODE_ACCESS_REJECT: + srv_log(sess, "Sending Access-Reject"); data->counters.access_rejects++; client->counters.access_rejects++; break; @@ -835,7 +1150,8 @@ static int radius_server_request(struct radius_server_data *data, wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { - perror("sendto[RADIUS SRV]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); } radius_msg_free(sess->last_reply); sess->last_reply = reply; @@ -890,7 +1206,8 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { - perror("recvfrom[radius_server]"); + wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", + strerror(errno)); goto fail; } @@ -971,6 +1288,140 @@ fail: } +static void radius_server_receive_acct(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct radius_server_data *data = eloop_ctx; + u8 *buf = NULL; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 sin6; +#endif /* CONFIG_IPV6 */ + } from; + socklen_t fromlen; + int len, res; + struct radius_client *client = NULL; + struct radius_msg *msg = NULL, *resp = NULL; + char abuf[50]; + int from_port = 0; + struct radius_hdr *hdr; + struct wpabuf *rbuf; + + buf = os_malloc(RADIUS_MAX_MSG_LEN); + if (buf == NULL) { + goto fail; + } + + fromlen = sizeof(from); + len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, + (struct sockaddr *) &from.ss, &fromlen); + if (len < 0) { + wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", + strerror(errno)); + goto fail; + } + +#ifdef CONFIG_IPV6 + if (data->ipv6) { + if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, + sizeof(abuf)) == NULL) + abuf[0] = '\0'; + from_port = ntohs(from.sin6.sin6_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, + (struct in_addr *) + &from.sin6.sin6_addr, 1); + } +#endif /* CONFIG_IPV6 */ + + if (!data->ipv6) { + os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); + from_port = ntohs(from.sin.sin_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, &from.sin.sin_addr, 0); + } + + RADIUS_DUMP("Received data", buf, len); + + if (client == NULL) { + RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); + data->counters.invalid_acct_requests++; + goto fail; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); + data->counters.malformed_acct_requests++; + client->counters.malformed_acct_requests++; + goto fail; + } + + os_free(buf); + buf = NULL; + + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(msg); + } + + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) { + RADIUS_DEBUG("Unexpected RADIUS code %d", + radius_msg_get_hdr(msg)->code); + data->counters.unknown_acct_types++; + client->counters.unknown_acct_types++; + goto fail; + } + + data->counters.acct_requests++; + client->counters.acct_requests++; + + if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret, + client->shared_secret_len)) { + RADIUS_DEBUG("Invalid Authenticator from %s", abuf); + data->counters.acct_bad_authenticators++; + client->counters.acct_bad_authenticators++; + goto fail; + } + + /* TODO: Write accounting information to a file or database */ + + hdr = radius_msg_get_hdr(msg); + + resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier); + if (resp == NULL) + goto fail; + + radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret, + client->shared_secret_len, + hdr->authenticator); + + RADIUS_DEBUG("Reply to %s:%d", abuf, from_port); + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(resp); + } + rbuf = radius_msg_get_buf(resp); + data->counters.acct_responses++; + client->counters.acct_responses++; + res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0, + (struct sockaddr *) &from.ss, fromlen); + if (res < 0) { + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); + } + +fail: + radius_msg_free(resp); + radius_msg_free(msg); + os_free(buf); +} + + static int radius_server_disable_pmtu_discovery(int s) { int r = -1; @@ -994,7 +1445,7 @@ static int radius_server_open_socket(int port) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno)); return -1; } @@ -1004,7 +1455,7 @@ static int radius_server_open_socket(int port) addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } @@ -1021,7 +1472,8 @@ static int radius_server_open_socket6(int port) s = socket(PF_INET6, SOCK_DGRAM, 0); if (s < 0) { - perror("socket[IPv6]"); + wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s", + strerror(errno)); return -1; } @@ -1030,7 +1482,7 @@ static int radius_server_open_socket6(int port) os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); addr.sin6_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } @@ -1183,8 +1635,8 @@ radius_server_read_clients(const char *client_file, int ipv6) break; } entry->shared_secret_len = os_strlen(entry->shared_secret); - entry->addr.s_addr = addr.s_addr; if (!ipv6) { + entry->addr.s_addr = addr.s_addr; val = 0; for (i = 0; i < mask; i++) val |= 1 << (31 - i); @@ -1241,8 +1693,7 @@ radius_server_init(struct radius_server_conf *conf) #ifndef CONFIG_IPV6 if (conf->ipv6) { - fprintf(stderr, "RADIUS server compiled without IPv6 " - "support.\n"); + wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support"); return NULL; } #endif /* CONFIG_IPV6 */ @@ -1251,7 +1702,8 @@ radius_server_init(struct radius_server_conf *conf) if (data == NULL) return NULL; - os_get_time(&data->start_time); + dl_list_init(&data->erp_keys); + os_get_reltime(&data->start_time); data->conf_ctx = conf->conf_ctx; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; @@ -1280,6 +1732,7 @@ radius_server_init(struct radius_server_conf *conf) data->tnc = conf->tnc; data->wps = conf->wps; data->pwd_group = conf->pwd_group; + data->server_id = conf->server_id; if (conf->eap_req_id_text) { data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len); if (data->eap_req_id_text) { @@ -1288,6 +1741,25 @@ radius_server_init(struct radius_server_conf *conf) data->eap_req_id_text_len = conf->eap_req_id_text_len; } } + data->erp = conf->erp; + data->erp_domain = conf->erp_domain; + + if (conf->subscr_remediation_url) { + data->subscr_remediation_url = + os_strdup(conf->subscr_remediation_url); + } + data->subscr_remediation_method = conf->subscr_remediation_method; + +#ifdef CONFIG_SQLITE + if (conf->sqlite_file) { + if (sqlite3_open(conf->sqlite_file, &data->db)) { + RADIUS_ERROR("Could not open SQLite file '%s'", + conf->sqlite_file); + radius_server_deinit(data); + return NULL; + } + } +#endif /* CONFIG_SQLITE */ #ifdef CONFIG_RADIUS_TEST if (conf->dump_msk_file) @@ -1297,7 +1769,7 @@ radius_server_init(struct radius_server_conf *conf) data->clients = radius_server_read_clients(conf->client_file, conf->ipv6); if (data->clients == NULL) { - printf("No RADIUS clients configured.\n"); + wpa_printf(MSG_ERROR, "No RADIUS clients configured"); radius_server_deinit(data); return NULL; } @@ -1309,8 +1781,7 @@ radius_server_init(struct radius_server_conf *conf) #endif /* CONFIG_IPV6 */ data->auth_sock = radius_server_open_socket(conf->auth_port); if (data->auth_sock < 0) { - printf("Failed to open UDP socket for RADIUS authentication " - "server\n"); + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server"); radius_server_deinit(data); return NULL; } @@ -1321,10 +1792,51 @@ radius_server_init(struct radius_server_conf *conf) return NULL; } + if (conf->acct_port) { +#ifdef CONFIG_IPV6 + if (conf->ipv6) + data->acct_sock = radius_server_open_socket6( + conf->acct_port); + else +#endif /* CONFIG_IPV6 */ + data->acct_sock = radius_server_open_socket(conf->acct_port); + if (data->acct_sock < 0) { + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server"); + radius_server_deinit(data); + return NULL; + } + if (eloop_register_read_sock(data->acct_sock, + radius_server_receive_acct, + data, NULL)) { + radius_server_deinit(data); + return NULL; + } + } else { + data->acct_sock = -1; + } + return data; } +/** + * radius_server_erp_flush - Flush all ERP keys + * @data: RADIUS server context from radius_server_init() + */ +void radius_server_erp_flush(struct radius_server_data *data) +{ + struct eap_server_erp_key *erp; + + if (data == NULL) + return; + while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key, + list)) != NULL) { + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); + } +} + + /** * radius_server_deinit - Deinitialize RADIUS server * @data: RADIUS server context from radius_server_init() @@ -1339,6 +1851,11 @@ void radius_server_deinit(struct radius_server_data *data) close(data->auth_sock); } + if (data->acct_sock >= 0) { + eloop_unregister_read_sock(data->acct_sock); + close(data->acct_sock); + } + radius_server_free_clients(data, data->clients); os_free(data->pac_opaque_encr_key); @@ -1348,6 +1865,15 @@ void radius_server_deinit(struct radius_server_data *data) #ifdef CONFIG_RADIUS_TEST os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ + os_free(data->subscr_remediation_url); + +#ifdef CONFIG_SQLITE + if (data->db) + sqlite3_close(data->db); +#endif /* CONFIG_SQLITE */ + + radius_server_erp_flush(data); + os_free(data); } @@ -1365,7 +1891,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, int ret, uptime; unsigned int idx; char *end, *pos; - struct os_time now; + struct os_reltime now; struct radius_client *cli; /* RFC 2619 - RADIUS Authentication Server MIB */ @@ -1376,7 +1902,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, pos = buf; end = buf + buflen; - os_get_time(&now); + os_get_reltime(&now); uptime = (now.sec - data->start_time.sec) * 100 + ((now.usec - data->start_time.usec) / 10000) % 100; ret = os_snprintf(pos, end - pos, @@ -1386,7 +1912,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, "radiusAuthServResetTime=0\n" "radiusAuthServConfigReset=4\n", uptime); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { *pos = '\0'; return pos - buf; } @@ -1402,7 +1928,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, "radiusAuthServTotalMalformedAccessRequests=%u\n" "radiusAuthServTotalBadAuthenticators=%u\n" "radiusAuthServTotalPacketsDropped=%u\n" - "radiusAuthServTotalUnknownTypes=%u\n", + "radiusAuthServTotalUnknownTypes=%u\n" + "radiusAccServTotalRequests=%u\n" + "radiusAccServTotalInvalidRequests=%u\n" + "radiusAccServTotalResponses=%u\n" + "radiusAccServTotalMalformedRequests=%u\n" + "radiusAccServTotalBadAuthenticators=%u\n" + "radiusAccServTotalUnknownTypes=%u\n", data->counters.access_requests, data->counters.invalid_requests, data->counters.dup_access_requests, @@ -1412,8 +1944,14 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, data->counters.malformed_access_requests, data->counters.bad_authenticators, data->counters.packets_dropped, - data->counters.unknown_types); - if (ret < 0 || ret >= end - pos) { + data->counters.unknown_types, + data->counters.acct_requests, + data->counters.invalid_acct_requests, + data->counters.acct_responses, + data->counters.malformed_acct_requests, + data->counters.acct_bad_authenticators, + data->counters.unknown_acct_types); + if (os_snprintf_error(end - pos, ret)) { *pos = '\0'; return pos - buf; } @@ -1426,7 +1964,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, if (inet_ntop(AF_INET6, &cli->addr6, abuf, sizeof(abuf)) == NULL) abuf[0] = '\0'; - if (inet_ntop(AF_INET6, &cli->mask6, abuf, + if (inet_ntop(AF_INET6, &cli->mask6, mbuf, sizeof(mbuf)) == NULL) mbuf[0] = '\0'; } @@ -1447,7 +1985,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, "radiusAuthServMalformedAccessRequests=%u\n" "radiusAuthServBadAuthenticators=%u\n" "radiusAuthServPacketsDropped=%u\n" - "radiusAuthServUnknownTypes=%u\n", + "radiusAuthServUnknownTypes=%u\n" + "radiusAccServTotalRequests=%u\n" + "radiusAccServTotalInvalidRequests=%u\n" + "radiusAccServTotalResponses=%u\n" + "radiusAccServTotalMalformedRequests=%u\n" + "radiusAccServTotalBadAuthenticators=%u\n" + "radiusAccServTotalUnknownTypes=%u\n", idx, abuf, mbuf, cli->counters.access_requests, @@ -1458,8 +2002,14 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, cli->counters.malformed_access_requests, cli->counters.bad_authenticators, cli->counters.packets_dropped, - cli->counters.unknown_types); - if (ret < 0 || ret >= end - pos) { + cli->counters.unknown_types, + cli->counters.acct_requests, + cli->counters.invalid_acct_requests, + cli->counters.acct_responses, + cli->counters.malformed_acct_requests, + cli->counters.acct_bad_authenticators, + cli->counters.unknown_acct_types); + if (os_snprintf_error(end - pos, ret)) { *pos = '\0'; return pos - buf; } @@ -1476,9 +2026,16 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, { struct radius_session *sess = ctx; struct radius_server_data *data = sess->server; - - return data->get_eap_user(data->conf_ctx, identity, identity_len, - phase2, user); + int ret; + + ret = data->get_eap_user(data->conf_ctx, identity, identity_len, + phase2, user); + if (ret == 0 && user) { + sess->accept_attr = user->accept_attr; + sess->remediation = user->remediation; + sess->macacl = user->macacl; + } + return ret; } @@ -1491,10 +2048,64 @@ static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) } +static void radius_server_log_msg(void *ctx, const char *msg) +{ + struct radius_session *sess = ctx; + srv_log(sess, "EAP: %s", msg); +} + + +#ifdef CONFIG_ERP + +static const char * radius_server_get_erp_domain(void *ctx) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + + return data->erp_domain; +} + + +static struct eap_server_erp_key * +radius_server_erp_get_key(void *ctx, const char *keyname) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} + + +static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + + dl_list_add(&data->erp_keys, &erp->list); + return 0; +} + +#endif /* CONFIG_ERP */ + + static struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, + .log_msg = radius_server_log_msg, +#ifdef CONFIG_ERP + .get_erp_send_reauth_start = NULL, + .get_erp_domain = radius_server_get_erp_domain, + .erp_get_key = radius_server_erp_get_key, + .erp_add_key = radius_server_erp_add_key, +#endif /* CONFIG_ERP */ }; @@ -1521,8 +2132,6 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) sess = s; break; } - if (sess) - break; } if (sess) break; diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 82466c30219f..ca4e38c12e99 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -21,6 +21,11 @@ struct radius_server_conf { */ int auth_port; + /** + * acct_port - UDP port to listen to as an accounting server + */ + int acct_port; + /** * client_file - RADIUS client configuration file * @@ -34,6 +39,11 @@ struct radius_server_conf { */ char *client_file; + /** + * sqlite_file - SQLite database for storing debug log information + */ + const char *sqlite_file; + /** * conf_ctx - Context pointer for callbacks * @@ -143,6 +153,23 @@ struct radius_server_conf { */ u16 pwd_group; + /** + * server_id - Server identity + */ + const char *server_id; + + /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + * + * This controls whether the authentication server derives ERP key + * hierarchy (rRK and rIK) from full EAP authentication and allows + * these keys to be used to perform ERP to derive rMSK instead of full + * EAP authentication to derive MSK. + */ + int erp; + + const char *erp_domain; + /** * wps - Wi-Fi Protected Setup context * @@ -199,12 +226,16 @@ struct radius_server_conf { #ifdef CONFIG_RADIUS_TEST const char *dump_msk_file; #endif /* CONFIG_RADIUS_TEST */ + + char *subscr_remediation_url; + u8 subscr_remediation_method; }; struct radius_server_data * radius_server_init(struct radius_server_conf *conf); +void radius_server_erp_flush(struct radius_server_data *data); void radius_server_deinit(struct radius_server_data *data); int radius_server_get_mib(struct radius_server_data *data, char *buf, diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/rsn_supp/Makefile +++ b/src/rsn_supp/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c index f2bac348bce7..79764d94b902 100644 --- a/src/rsn_supp/peerkey.c +++ b/src/rsn_supp/peerkey.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -65,6 +65,7 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, { 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; @@ -79,6 +80,7 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, (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 | @@ -112,8 +114,8 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, "(mui %d error_type %d)", mui, error_type); } - wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, - rbuf, rlen, err->key_mic); + wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, err192->key_mic); return 0; } @@ -126,6 +128,7 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, { size_t rlen; struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; u8 *rbuf, *pos; size_t kde_len; u16 key_info; @@ -140,6 +143,7 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, (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 | @@ -164,8 +168,8 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, 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, ver, src_addr, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + 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; } @@ -217,23 +221,17 @@ static int wpa_supplicant_process_smk_m2( return -1; } - cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; - if (cipher & WPA_CIPHER_CCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); - cipher = WPA_CIPHER_CCMP; - } else if (cipher & WPA_CIPHER_GCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); - cipher = WPA_CIPHER_GCMP; - } else if (cipher & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); - cipher = WPA_CIPHER_TKIP; - } else { + 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 @@ -246,11 +244,7 @@ static int wpa_supplicant_process_smk_m2( os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peerkey->rsnie_i_len = kde.rsn_ie_len; peerkey->cipher = cipher; -#ifdef CONFIG_IEEE80211W - if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256)) - peerkey->use_sha256 = 1; -#endif /* CONFIG_IEEE80211W */ + peerkey->akmp = ie.key_mgmt; if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -294,14 +288,14 @@ static int wpa_supplicant_process_smk_m2( * @mac_p: Peer MAC address * @inonce: Initiator Nonce * @mac_i: Initiator MAC address - * @use_sha256: Whether to use SHA256-based KDF + * @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 use_sha256) + int akmp) { char *title = "SMK Name"; const u8 *addr[5]; @@ -316,7 +310,7 @@ static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, addr[4] = mac_i; #ifdef CONFIG_IEEE80211W - if (use_sha256) + if (wpa_key_mgmt_sha256(akmp)) hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); else #endif /* CONFIG_IEEE80211W */ @@ -377,7 +371,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, + wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL, mbuf, mlen, NULL); } @@ -432,8 +426,9 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, - ETH_P_EAPOL, mbuf, mlen, msg->key_mic); + wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver, + peerkey->addr, ETH_P_EAPOL, mbuf, mlen, + msg->key_mic); } @@ -496,17 +491,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, peerkey->rsnie_p_len = kde->rsn_ie_len; os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); - cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; - if (cipher & WPA_CIPHER_CCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); - peerkey->cipher = WPA_CIPHER_CCMP; - } else if (cipher & WPA_CIPHER_GCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); - peerkey->cipher = WPA_CIPHER_GCMP; - } else if (cipher & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); - peerkey->cipher = WPA_CIPHER_TKIP; - } else { + 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, @@ -515,6 +502,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, /* TODO: abort negotiation */ return -1; } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; return 0; } @@ -527,7 +517,6 @@ static int wpa_supplicant_process_smk_m45( struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; u32 lifetime; - struct os_time now; if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " @@ -579,22 +568,20 @@ static int wpa_supplicant_process_smk_m45( lifetime = WPA_GET_BE32(kde.lifetime); wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); if (lifetime > 1000000000) - lifetime = 1000000000; /* avoid overflowing expiration time */ + lifetime = 1000000000; /* avoid overflowing eloop time */ peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + 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->use_sha256); + 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->use_sha256); + peerkey->akmp); } wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); @@ -667,11 +654,11 @@ static int wpa_supplicant_process_smk_error( 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse ie; - const u8 *kde; - size_t len, kde_buf_len; + size_t kde_buf_len; struct wpa_ptk *stk; u8 buf[8], *kde_buf, *pos; be32 lifetime; @@ -682,14 +669,13 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, os_memset(&ie, 0, sizeof(ie)); /* RSN: msg 1/4 should contain SMKID for the selected SMK */ - kde = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); - if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { + 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(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + 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; @@ -709,12 +695,11 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", sm->own_addr, peerkey->addr, peerkey->pnonce, key->key_nonce, - (u8 *) stk, sizeof(*stk), - peerkey->use_sha256); + stk, peerkey->akmp, peerkey->cipher); /* Supplicant: swap tx/rx Mic keys */ - os_memcpy(buf, stk->u.auth.tx_mic_key, 8); - os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); - os_memcpy(stk->u.auth.rx_mic_key, buf, 8); + 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 + @@ -747,7 +732,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, struct wpa_eapol_ie_parse *kde) { u32 lifetime; - struct os_time now; if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) return; @@ -766,8 +750,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, lifetime, peerkey->lifetime); peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + lifetime; eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); @@ -777,11 +759,10 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse kde; - const u8 *keydata; - size_t len; wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); @@ -790,16 +771,14 @@ static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE * from the peer. It may also include Lifetime KDE. */ - keydata = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); - if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || + 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(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + 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; @@ -826,11 +805,11 @@ static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse kde; - const u8 *keydata; - size_t len, key_len; + size_t key_len; const u8 *_key; u8 key_buf[32], rsc[6]; @@ -841,10 +820,8 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include * Lifetime KDE. */ - keydata = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); - if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { + 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; @@ -875,15 +852,15 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, WPA_GET_BE16(key->key_info), - NULL, 0, &peerkey->stk)) + &peerkey->stk)) return; - _key = (u8 *) peerkey->stk.tk1; + _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, peerkey->stk.u.auth.rx_mic_key, 8); - os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); + os_memcpy(key_buf + 16, _key + 24, 8); + os_memcpy(key_buf + 24, _key + 16, 8); _key = key_buf; key_len = 32; } else @@ -892,10 +869,12 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, 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)); } @@ -911,7 +890,7 @@ static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, os_memset(rsc, 0, 6); if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, - rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1, + 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."); @@ -932,27 +911,27 @@ static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, */ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 ver, + struct wpa_eapol_key_192 *key, u16 ver, const u8 *buf, size_t len) { - u8 mic[16]; + 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, - (u8 *) &peerkey->stk, sizeof(peerkey->stk), - peerkey->use_sha256); + &peerkey->stk, peerkey->akmp, peerkey->cipher); peerkey->stk_set = 1; } - os_memcpy(mic, key->key_mic, 16); + os_memcpy(mic, key->key_mic, mic_len); if (peerkey->tstk_set) { - os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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 { @@ -961,14 +940,15 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, 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, 16); - wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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; @@ -1037,10 +1017,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) return -1; peerkey->initiator = 1; os_memcpy(peerkey->addr, peer, ETH_ALEN); -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(sm->key_mgmt)) - peerkey->use_sha256 = 1; -#endif /* CONFIG_IEEE80211W */ + 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, @@ -1107,8 +1084,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " MACSTR ")", MAC2STR(peer)); - wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, - rbuf, rlen, req->key_mic); + 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; @@ -1127,28 +1104,32 @@ void peerkey_deinit(struct wpa_sm *sm) while (peerkey) { prev = peerkey; peerkey = peerkey->next; - os_free(prev); + 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) + 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); + 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); + 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); + wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver, + key_data, key_data_len); } } diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h index b8845f710072..6ccd948baace 100644 --- a/src/rsn_supp/peerkey.h +++ b/src/rsn_supp/peerkey.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -24,11 +24,10 @@ struct wpa_peerkey { int smk_complete; u8 smkid[PMKID_LEN]; u32 lifetime; - os_time_t expiration; int cipher; /* Selected cipher (WPA_CIPHER_*) */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; int replay_counter_set; - int use_sha256; /* whether AKMP indicate SHA256-based derivations */ + int akmp; struct wpa_ptk stk, tstk; int stk_set, tstk_set; @@ -39,10 +38,11 @@ struct wpa_peerkey { int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 ver, + 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); + 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); @@ -61,7 +61,8 @@ peerkey_verify_eapol_key_mic(struct wpa_sm *sm, 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) + struct wpa_eapol_key *key, u16 key_info, u16 ver, + const u8 *key_data, size_t key_data_len) { } diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index df675834c6f4..ef7b68386476 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - RSN PMKSA cache - * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen + * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,7 +15,7 @@ #include "wpa_i.h" #include "pmksa_cache.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL static const int pmksa_cache_max_entries = 32; @@ -35,7 +35,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { - os_free(entry); + bin_clear_free(entry, sizeof(*entry)); } @@ -53,9 +53,9 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&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; @@ -80,13 +80,13 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; struct rsn_pmksa_cache_entry *entry; - struct os_time now; + 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_time(&now); + os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; @@ -109,6 +109,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @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 @@ -122,22 +124,31 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) */ 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_time now; + 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; - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); - os_get_time(&now); + 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; @@ -152,9 +163,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, while (pos) { if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { if (pos->pmk_len == pmk_len && - os_memcmp(pos->pmk, pmk, pmk_len) == 0 && - os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == - 0) { + 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); @@ -164,17 +175,23 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, pmksa->pmksa = pos->next; else prev->next = pos->next; - wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " - "the current AP"); - pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); /* * 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. + * 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(). */ - pmksa_cache_flush(pmksa, network_ctx); + 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; @@ -235,15 +252,22 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, * 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) +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) { + 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) @@ -320,6 +344,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, 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) @@ -453,13 +478,13 @@ 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_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic\n"); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; i = 0; @@ -468,7 +493,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", i, MAC2STR(entry->aa)); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, @@ -476,7 +501,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, " %d %d\n", (int) (entry->expiration - now.sec), entry->opportunistic); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; entry = entry->next; @@ -509,4 +534,4 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, return pmksa; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index f318c52fa5bc..f8e040e067d9 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -44,7 +44,7 @@ enum pmksa_free_reason { PMKSA_EXPIRE, }; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -57,6 +57,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, 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); @@ -66,13 +67,14 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, 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); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int reason), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; @@ -103,6 +105,7 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, 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; @@ -121,10 +124,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, } static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, - void *network_ctx) + void *network_ctx, + const u8 *pmk, size_t pmk_len) { } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index ab61867b4071..c6534af2c96e 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -1,6 +1,6 @@ /* * RSN pre-authentication (supplicant) - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,7 +18,7 @@ #include "wpa_i.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL #define PMKID_CANDIDATE_PRIO_SCAN 1000 @@ -70,13 +70,14 @@ static void rsn_preauth_receive(void *ctx, const u8 *src_addr, } -static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, +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 (success) { + if (result == EAPOL_SUPP_RESULT_SUCCESS) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(eapol, pmk, PMK_LEN); @@ -93,6 +94,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, 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); @@ -100,13 +102,14 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " "pre-auth EAPOL state machines"); - success = 0; + result = EAPOL_SUPP_RESULT_FAILURE; } } wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR " %s", MAC2STR(sm->preauth_bssid), - success ? "completed successfully" : "failed"); + result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : + "failed"); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); @@ -169,6 +172,7 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, { struct eapol_config eapol_conf; struct eapol_ctx *ctx; + int ret; if (sm->preauth_eapol) return -1; @@ -194,14 +198,16 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " "packet processing (bridge) for " "pre-authentication"); - return -2; + ret = -2; + goto fail; } } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); - return -4; + ret = -4; + goto fail; } ctx->ctx = sm->ctx->ctx; ctx->msg_ctx = sm->ctx->ctx; @@ -219,7 +225,8 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, os_free(ctx); wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " "state machines for pre-authentication"); - return -3; + ret = -3; + goto fail; } os_memset(&eapol_conf, 0, sizeof(eapol_conf)); eapol_conf.accept_802_1x_keys = 0; @@ -244,6 +251,15 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, 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; } @@ -296,7 +312,9 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) 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_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 */ @@ -389,6 +407,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, 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; @@ -485,7 +515,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, if (sm->preauth_eapol) { ret = os_snprintf(pos, end - pos, "Pre-authentication " "EAPOL state machines:\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; res = eapol_sm_get_status(sm->preauth_eapol, @@ -508,4 +538,4 @@ int rsn_preauth_in_progress(struct wpa_sm *sm) return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h index 27d3112cd94e..277f0663b0f0 100644 --- a/src/rsn_supp/preauth.h +++ b/src/rsn_supp/preauth.h @@ -11,7 +11,7 @@ struct wpa_scan_results; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, @@ -27,7 +27,7 @@ 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 and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -74,6 +74,6 @@ static inline int rsn_preauth_in_progress(struct wpa_sm *sm) return 0; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PREAUTH_H */ diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 7646ca88671a..c1d7749191d7 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -33,12 +33,15 @@ #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) #define TDLS_TESTING_DECLINE_RESP BIT(9) #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) +#define TDLS_TESTING_WRONG_MIC BIT(11) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ #define TPK_LIFETIME 43200 /* 12 hours */ -#define TPK_RETRY_COUNT 3 -#define TPK_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M1_RETRY_COUNT 3 +#define TPK_M1_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M2_RETRY_COUNT 10 +#define TPK_M2_TIMEOUT 500 /* in milliseconds */ #define TDLS_MIC_LEN 16 @@ -79,6 +82,10 @@ struct wpa_tdls_frame { static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer); +static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, + u16 reason_code); #define TDLS_MAX_IE_LEN 80 @@ -86,6 +93,7 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); struct wpa_tdls_peer { struct wpa_tdls_peer *next; + unsigned int reconfig_key:1; int initiator; /* whether this end was initiator for TDLS setup */ u8 addr[ETH_ALEN]; /* other end MAC address */ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ @@ -104,6 +112,7 @@ struct wpa_tdls_peer { } tpk; int tpk_set; int tpk_success; + int tpk_in_progress; struct tpk_timer { u8 dest[ETH_ALEN]; @@ -112,6 +121,7 @@ struct wpa_tdls_peer { u8 action_code; /* TDLS frame type */ u8 dialog_token; u16 status_code; + u32 peer_capab; int buf_len; /* length of TPK message for retransmission */ u8 *buf; /* buffer for TPK message */ } sm_tmr; @@ -120,6 +130,27 @@ struct wpa_tdls_peer { u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; + + u8 qos_info; + + u16 aid; + + u8 *ext_capab; + size_t ext_capab_len; + + u8 *supp_channels; + size_t supp_channels_len; + + u8 *supp_oper_classes; + size_t supp_oper_classes_len; + + u8 wmm_capable; + + /* channel switch currently enabled */ + int chan_switch_enabled; }; @@ -189,26 +220,30 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len) + u16 status_code, u32 peer_capab, + int initiator, const u8 *buf, size_t len) { return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, - status_code, buf, len); + status_code, peer_capab, initiator, buf, + len); } static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, - u8 dialog_token, u16 status_code, - const u8 *msg, size_t msg_len) + u8 dialog_token, u16 status_code, u32 peer_capab, + int initiator, const u8 *msg, size_t msg_len) { struct wpa_tdls_peer *peer; wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " - "dialog_token=%u status_code=%u msg_len=%u", + "dialog_token=%u status_code=%u peer_capab=%u initiator=%d " + "msg_len=%u", MAC2STR(dest), action_code, dialog_token, status_code, - (unsigned int) msg_len); + peer_capab, initiator, (unsigned int) msg_len); if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, - status_code, msg, msg_len)) { + status_code, peer_capab, initiator, msg, + msg_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to send message " "(action_code=%u)", action_code); return -1; @@ -233,14 +268,20 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - peer->sm_tmr.count = TPK_RETRY_COUNT; - peer->sm_tmr.timer = TPK_TIMEOUT; + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + peer->sm_tmr.count = TPK_M2_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M2_TIMEOUT; + } else { + peer->sm_tmr.count = TPK_M1_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M1_TIMEOUT; + } /* Copy message to resend on timeout */ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); peer->sm_tmr.action_code = action_code; peer->sm_tmr.dialog_token = dialog_token; peer->sm_tmr.status_code = status_code; + peer->sm_tmr.peer_capab = peer_capab; peer->sm_tmr.buf_len = msg_len; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = os_malloc(msg_len); @@ -250,28 +291,21 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); return 0; } static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, - u16 reason_code, int free_peer) + u16 reason_code) { int ret; - if (sm->tdls_external_setup) { - ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); - - /* disable the link after teardown was sent */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); - } else { - ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); - } - - if (sm->tdls_external_setup || free_peer) - wpa_tdls_peer_free(sm, peer); + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + /* disable the link after teardown was sent */ + wpa_tdls_disable_peer_link(sm, peer); return ret; } @@ -285,7 +319,6 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) if (peer->sm_tmr.count) { peer->sm_tmr.count--; - peer->sm_tmr.timer = TPK_TIMEOUT; wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " "(action_code=%u)", @@ -305,6 +338,8 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) peer->sm_tmr.action_code, peer->sm_tmr.dialog_token, peer->sm_tmr.status_code, + peer->sm_tmr.peer_capab, + peer->initiator, peer->sm_tmr.buf, peer->sm_tmr.buf_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to retry " @@ -312,14 +347,15 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); } else { eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); wpa_tdls_do_teardown(sm, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } @@ -535,7 +571,7 @@ static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, peer->rsnie_p, timeoutie, (u8 *) ftie, mic); - if (os_memcmp(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " "dropping packet"); wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", @@ -562,7 +598,7 @@ static int wpa_supplicant_verify_tdls_mic_teardown( if (peer->tpk_set) { wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, dtoken, lnkid, (u8 *) ftie, mic); - if (os_memcmp(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " "dropping packet"); return -1; @@ -597,29 +633,78 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); wpa_tdls_do_teardown(sm, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } -static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + struct wpa_tdls_peer *cur, *prev; + + cur = sm->tdls; + prev = NULL; + while (cur && cur != peer) { + prev = cur; + cur = cur->next; + } + + if (cur != peer) { + wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR + " to remove it from the list", + MAC2STR(peer->addr)); + return; + } + + if (prev) + prev->next = peer->next; + else + sm->tdls = peer->next; +} + + +static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, MAC2STR(peer->addr)); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + peer->reconfig_key = 0; peer->initiator = 0; + peer->tpk_in_progress = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; + os_free(peer->ht_capabilities); + peer->ht_capabilities = NULL; + os_free(peer->vht_capabilities); + peer->vht_capabilities = NULL; + os_free(peer->ext_capab); + peer->ext_capab = NULL; + os_free(peer->supp_channels); + peer->supp_channels = NULL; + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = NULL; peer->rsnie_i_len = peer->rsnie_p_len = 0; peer->cipher = 0; + peer->qos_info = 0; + peer->wmm_capable = 0; peer->tpk_set = peer->tpk_success = 0; + peer->chan_switch_enabled = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); os_memset(peer->rnonce, 0, WPA_NONCE_LEN); } +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + wpa_tdls_peer_clear(sm, peer); + wpa_tdls_peer_remove_from_list(sm, peer); + os_free(peer); +} + + static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, struct wpa_tdls_lnkid *lnkid) { @@ -636,7 +721,8 @@ static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, } -int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, + u16 reason_code) { struct wpa_tdls_peer *peer; struct wpa_tdls_ftie *ftie; @@ -660,6 +746,13 @@ int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) return 0; } + /* Cancel active channel switch before teardown */ + if (peer->chan_switch_enabled) { + wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR + " to base channel", MAC2STR(addr)); + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); + } + dialog_token = peer->dtoken; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, @@ -715,13 +808,9 @@ skip_ies: /* request driver to send Teardown using this FTIE */ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf, - pos - rbuf); + reason_code, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); - /* clear the Peerkey statemachine */ - wpa_tdls_peer_free(sm, peer); - return 0; } @@ -750,11 +839,19 @@ int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) return -1; } - return wpa_tdls_do_teardown(sm, peer, reason_code, 0); + return wpa_tdls_do_teardown(sm, peer, reason_code); +} + + +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_free(sm, peer); } -void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) +void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; @@ -763,10 +860,49 @@ void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) break; } - if (peer) { + if (!peer || !peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " not connected - cannot teardown unreachable link", + MAC2STR(addr)); + return; + } + + if (wpa_tdls_is_external_setup(sm)) { + /* + * Get us on the base channel, disable the link, send a + * teardown packet through the AP, and then reset link data. + */ + if (peer->chan_switch_enabled) + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); + wpa_tdls_send_teardown(sm, addr, + WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE); wpa_tdls_peer_free(sm, peer); + } else { + wpa_tdls_disable_peer_link(sm, peer); + } +} + + +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return "disabled"; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; } + + if (peer == NULL) + return "peer does not exist"; + + if (!peer->tpk_success) + return "peer not connected"; + + return "connected"; } @@ -803,10 +939,15 @@ static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, " (reason code %u)", MAC2STR(src_addr), reason_code); ielen = len - (pos - buf); /* start of IE in buf */ - if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); - return -1; - } + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs may add arbitrary padding to the + * end of short TDLS frames and that would look like invalid IEs. + */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround"); if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " @@ -839,11 +980,7 @@ skip_ftie: * Request the driver to disable the direct link and clear associated * keys. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - - /* clear the Peerkey statemachine */ - wpa_tdls_peer_free(sm, peer); - + wpa_tdls_disable_peer_link(sm, peer); return 0; } @@ -853,25 +990,37 @@ skip_ftie: * appropriate status code mentioning reason for error/failure. * @dst - MAC addr of Peer station * @tdls_action - TDLS frame type for which error code is sent + * @initiator - was this end the initiator of the connection * @status - status code mentioning reason */ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, - u8 tdls_action, u8 dialog_token, u16 status) + u8 tdls_action, u8 dialog_token, int initiator, + u16 status) { wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR " (action=%u status=%u)", MAC2STR(dst), tdls_action, status); return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, - NULL, 0); + 0, initiator, NULL, 0); } static struct wpa_tdls_peer * -wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr) +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing) { struct wpa_tdls_peer *peer; + if (existing) + *existing = 0; + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) { + if (existing) + *existing = 1; + return peer; /* re-use existing entry */ + } + } + wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, MAC2STR(addr)); @@ -897,6 +1046,7 @@ static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; + int status; if (!wpa_tdls_get_privacy(sm)) { wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); @@ -1057,11 +1207,11 @@ skip_ies: "Handshake Message 1 (peer " MACSTR ")", MAC2STR(peer->addr)); - wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, + 1, 0, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1075,6 +1225,7 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, u32 lifetime; struct wpa_tdls_timeoutie timeoutie; struct wpa_tdls_ftie *ftie; + int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1138,13 +1289,20 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_MIC) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC"); + ftie->mic[0] ^= 0x01; + } +#endif /* CONFIG_TDLS_TESTING */ skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, + dtoken, 0, 0, peer->initiator, rbuf, + pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1158,6 +1316,8 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie timeoutie; u32 lifetime; + int status; + u32 peer_capab = 0; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1219,13 +1379,28 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_MIC) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC"); + ftie->mic[0] ^= 0x01; + } +#endif /* CONFIG_TDLS_TESTING */ skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, - rbuf, pos - rbuf); + + if (peer->vht_capabilities) + peer_capab |= TDLS_PEER_VHT; + if (peer->ht_capabilities) + peer_capab |= TDLS_PEER_HT; + if (peer->wmm_capable) + peer_capab |= TDLS_PEER_WMM; + + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, + dtoken, 0, peer_capab, peer->initiator, + rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1233,11 +1408,85 @@ static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 dialog_token) { + size_t buf_len = 0; + struct wpa_tdls_timeoutie timeoutie; + u16 rsn_capab; + u8 *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + int status; + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " "(peer " MACSTR ")", MAC2STR(peer->addr)); + if (!wpa_tdls_get_privacy(sm)) + goto skip_rsn_ies; + + /* Filling RSN IE */ + hdr = (struct rsn_ie_hdr *) peer->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + count = 0; + + /* + * AES-CCMP is the default encryption preferred for TDLS, so + * RSN IE is filled only with CCMP cipher suite. + * Note: TKIP is not used to encrypt TDLS link. + * + * Regardless of the cipher used on the AP connection, select CCMP + * here. + */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count++; + WPA_PUT_LE16(count_pos, count); + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; + hdr->len = (pos - (u8 *) hdr) - 2; + peer->rsnie_i_len = pos - peer->rsnie_i; + + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response", + (u8 *) hdr, hdr->len + 2); +skip_rsn_ies: + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, Lifetime */ + buf_len += peer->rsnie_i_len + + sizeof(struct wpa_tdls_timeoutie); + } + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) { + wpa_tdls_peer_free(sm, peer); + return -1; + } + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); + /* Lifetime */ + peer->lifetime = TPK_LIFETIME; + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), peer->lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); +skip_ies: + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, + dialog_token, 0, 0, 0, rbuf, pos - rbuf); + os_free(rbuf); - return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, - dialog_token, 0, NULL, 0); + return status; } @@ -1263,10 +1512,17 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, dialog_token = buf[sizeof(struct wpa_tdls_frame)]; + /* + * Some APs will tack on a weird IE to the end of a TDLS + * discovery request packet. This needn't fail the response, + * since the required IE are verified separately. + */ if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, len - (sizeof(struct wpa_tdls_frame) + 1), - &kde) < 0) - return -1; + &kde) < 0) { + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround"); + } if (!kde.lnkid) { wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " @@ -1282,7 +1538,7 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, return -1; } - peer = wpa_tdls_add_peer(sm, addr); + peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; @@ -1298,7 +1554,7 @@ int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " MACSTR, MAC2STR(addr)); return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, - 1, 0, NULL, 0); + 1, 0, 0, 1, NULL, 0); } @@ -1309,25 +1565,189 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); return -1; } + peer->supp_rates_len = merge_byte_arrays( + peer->supp_rates, sizeof(peer->supp_rates), + kde->supp_rates + 2, kde->supp_rates_len - 2, + kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, + kde->ext_supp_rates_len - 2); + return 0; +} + + +static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ht_capabilities || + kde->ht_capabilities_len < + sizeof(struct ieee80211_ht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " + "received"); + return 0; + } + + if (!peer->ht_capabilities) { + peer->ht_capabilities = + os_zalloc(sizeof(struct ieee80211_ht_capabilities)); + if (peer->ht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->ht_capabilities, kde->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities", + (u8 *) peer->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + + return 0; +} + + +static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->vht_capabilities || + kde->vht_capabilities_len < + sizeof(struct ieee80211_vht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " + "received"); + return 0; + } + + if (!peer->vht_capabilities) { + peer->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (peer->vht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->vht_capabilities, kde->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities", + (u8 *) peer->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + + return 0; +} + + +static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ext_capab) { + wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities " + "received"); + return 0; + } + + if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) { + /* Need to allocate buffer to fit the new information */ + os_free(peer->ext_capab); + peer->ext_capab = os_zalloc(kde->ext_capab_len - 2); + if (peer->ext_capab == NULL) + return -1; + } + + peer->ext_capab_len = kde->ext_capab_len - 2; + os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len); + + return 0; +} + + +static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + struct wmm_information_element *wmm; + + if (!kde->wmm) { + wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received"); + return 0; + } + + if (kde->wmm_len < sizeof(struct wmm_information_element)) { + wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received"); + return -1; + } + + wmm = (struct wmm_information_element *) kde->wmm; + peer->qos_info = wmm->qos_info; + + peer->wmm_capable = 1; + + wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info); + return 0; +} + + +static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_channels) { + wpa_printf(MSG_DEBUG, "TDLS: No supported channels received"); + return 0; + } + + if (!peer->supp_channels || + peer->supp_channels_len < kde->supp_channels_len) { + os_free(peer->supp_channels); + peer->supp_channels = os_zalloc(kde->supp_channels_len); + if (peer->supp_channels == NULL) + return -1; + } - peer->supp_rates_len = kde->supp_rates_len - 2; - if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES) - peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES; - os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len); + peer->supp_channels_len = kde->supp_channels_len; - if (kde->ext_supp_rates) { - int clen = kde->ext_supp_rates_len - 2; - if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES) - clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len; - os_memcpy(peer->supp_rates + peer->supp_rates_len, - kde->ext_supp_rates + 2, clen); - peer->supp_rates_len += clen; + os_memcpy(peer->supp_channels, kde->supp_channels, + peer->supp_channels_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels", + (u8 *) peer->supp_channels, peer->supp_channels_len); + return 0; +} + + +static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_oper_classes) { + wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received"); + return 0; + } + + if (!peer->supp_oper_classes || + peer->supp_oper_classes_len < kde->supp_oper_classes_len) { + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len); + if (peer->supp_oper_classes == NULL) + return -1; } + peer->supp_oper_classes_len = kde->supp_oper_classes_len; + os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes, + peer->supp_oper_classes_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes", + (u8 *) peer->supp_oper_classes, + peer->supp_oper_classes_len); return 0; } +static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + int add) +{ + return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid, + peer->capability, + peer->supp_rates, peer->supp_rates_len, + peer->ht_capabilities, + peer->vht_capabilities, + peer->qos_info, peer->wmm_capable, + peer->ext_capab, peer->ext_capab_len, + peer->supp_channels, + peer->supp_channels_len, + peer->supp_oper_classes, + peer->supp_oper_classes_len); +} + + static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { @@ -1363,17 +1783,44 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) { - existing_peer = 1; - break; - } - } + peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer); + if (peer == NULL) + goto error; - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, src_addr); - if (peer == NULL) - goto error; + /* If found, use existing entry instead of adding a new one; + * how to handle the case where both ends initiate at the + * same time? */ + if (existing_peer) { + if (peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " + "direct link is enabled - tear down the " + "old link first"); + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_clear(sm, peer); + } else if (peer->initiator) { + /* + * An entry is already present, so check if we already + * sent a TDLS Setup Request. If so, compare MAC + * addresses and let the STA with the lower MAC address + * continue as the initiator. The other negotiation is + * terminated. + */ + if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard request " + "from peer with higher address " + MACSTR, MAC2STR(src_addr)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TDLS: Accept request " + "from peer with lower address " + MACSTR " (terminate previously " + "initiated negotiation", + MAC2STR(src_addr)); + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, + peer->addr); + wpa_tdls_peer_clear(sm, peer); + } + } } /* capability information */ @@ -1381,10 +1828,15 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, cpos += 2; ielen = len - (cpos - buf); /* start of IE in buf */ - if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); - goto error; - } + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs may add arbitrary padding to the + * end of short TDLS frames and that would look like invalid IEs. + */ + if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround"); if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " @@ -1396,7 +1848,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); - status = WLAN_STATUS_NOT_IN_SAME_BSS; + status = WLAN_STATUS_REQUEST_DECLINED; goto error; } @@ -1406,20 +1858,39 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, if (copy_supp_rates(&kde, peer) < 0) goto error; + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + /* Overwrite with the qos_info obtained in WMM IE */ + if (copy_peer_wmm_capab(&kde, peer) < 0) + goto error; + + peer->aid = kde.aid; + #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) - break; - } - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, src_addr); - if (peer == NULL) - goto error; - } + peer = wpa_tdls_add_peer(sm, src_addr, NULL); + if (peer == NULL) + goto error; wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, + NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0); wpa_tdls_send_tpk_m1(sm, peer); } @@ -1502,52 +1973,6 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, } skip_rsn: - /* If found, use existing entry instead of adding a new one; - * how to handle the case where both ends initiate at the - * same time? */ - if (existing_peer) { - if (peer->tpk_success) { - wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " - "direct link is enabled - tear down the " - "old link first"); -#if 0 - /* TODO: Disabling the link would be more proper - * operation here, but it seems to trigger a race with - * some drivers handling the new request frame. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); -#else - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, - src_addr); - else - wpa_tdls_del_key(sm, peer); -#endif - wpa_tdls_peer_free(sm, peer); - } - - /* - * An entry is already present, so check if we already sent a - * TDLS Setup Request. If so, compare MAC addresses and let the - * STA with the lower MAC address continue as the initiator. - * The other negotiation is terminated. - */ - if (peer->initiator) { - if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { - wpa_printf(MSG_DEBUG, "TDLS: Discard request " - "from peer with higher address " - MACSTR, MAC2STR(src_addr)); - return -1; - } else { - wpa_printf(MSG_DEBUG, "TDLS: Accept request " - "from peer with lower address " - MACSTR " (terminate previously " - "initiated negotiation", - MAC2STR(src_addr)); - wpa_tdls_peer_free(sm, peer); - } - } - } - #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { @@ -1572,16 +1997,26 @@ skip_rsn: } ftie = (struct wpa_tdls_ftie *) kde.ftie; - os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; - if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, - "TDLS: Failed to get random data for responder nonce"); - wpa_tdls_peer_free(sm, peer); - goto error; + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + /* + * There is no point in updating the RNonce for every obtained + * TPK M1 frame (e.g., retransmission due to timeout) with the + * same INonce (SNonce in FTIE). However, if the TPK M1 is + * retransmitted with a different INonce, update the RNonce + * since this is for a new TDLS session. + */ + wpa_printf(MSG_DEBUG, + "TDLS: New TPK M1 INonce - generate new RNonce"); + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "TDLS: Failed to get random data for responder nonce"); + goto error; + } } #if 0 @@ -1634,27 +2069,41 @@ skip_rsn: wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); skip_rsn_check: - /* add the peer to the driver as a "setup in progress" peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) + goto skip_add_peer; +#endif /* CONFIG_TDLS_TESTING */ + + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_tdls_addset_peer(sm, peer, 1) < 0) + goto error; + +#ifdef CONFIG_TDLS_TESTING +skip_add_peer: +#endif /* CONFIG_TDLS_TESTING */ + peer->tpk_in_progress = 1; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); goto error; } return 0; error: - wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, status); + if (peer) + wpa_tdls_peer_free(sm, peer); return -1; } -static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; + peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); if (wpa_tdls_get_privacy(sm)) { u32 lifetime = peer->lifetime; @@ -1675,11 +2124,14 @@ static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) #endif /* CONFIG_TDLS_TESTING */ } - /* add supported rates and capabilities to the TDLS peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, - peer->supp_rates, peer->supp_rates_len); + if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) { + wpa_printf(MSG_INFO, "TDLS: Could not configure key to the " + "driver"); + return -1; + } + peer->reconfig_key = 0; - wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); + return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); } @@ -1698,6 +2150,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, int ielen; u16 status; const u8 *pos; + int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " "(Peer " MACSTR ")", MAC2STR(src_addr)); @@ -1710,10 +2163,23 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, "TPK M2: " MACSTR, MAC2STR(src_addr)); return -1; } + if (!peer->initiator) { + /* + * This may happen if both devices try to initiate TDLS at the + * same time and we accept the TPK M1 from the peer in + * wpa_tdls_process_tpk_m1() and clear our previous state. + */ + wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so " + "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); + return -1; + } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); - if (len < 3 + 2 + 1) + if (len < 3 + 2 + 1) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } + pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); @@ -1722,8 +2188,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1734,18 +2199,25 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); - if (len < 3 + 2 + 1 + 2) + if (len < 3 + 2 + 1 + 2) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } /* capability information */ peer->capability = WPA_GET_LE16(pos); pos += 2; ielen = len - (pos - buf); /* start of IE in buf */ - if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); - goto error; - } + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs may add arbitrary padding to the + * end of short TDLS frames and that would look like invalid IEs. + */ + if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround"); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { @@ -1773,6 +2245,29 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, if (copy_supp_rates(&kde, peer) < 0) goto error; + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + /* Overwrite with the qos_info obtained in WMM IE */ + if (copy_peer_wmm_capab(&kde, peer) < 0) + goto error; + + peer->aid = kde.aid; + if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; @@ -1788,6 +2283,13 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); + if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { + wpa_printf(MSG_INFO, + "TDLS: Too long Responder RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + /* * FIX: bitwise comparison of RSN IE is not the correct way of * validation this. It can be different, but certain fields must @@ -1863,30 +2365,52 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, (u8 *) timeoutie, ftie) < 0) { /* Discard the frame */ wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } - wpa_tdls_set_key(sm, peer); + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } skip_rsn: peer->dtoken = dtoken; + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_tdls_addset_peer(sm, peer, 0) < 0) + goto error; + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); - wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); - - wpa_tdls_enable_link(sm, peer); + if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) + goto error; - return 0; + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M2 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + wpa_tdls_do_teardown( + sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } + } + return ret; error: - wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1, status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1903,6 +2427,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, u16 status; const u8 *pos; u32 lifetime; + int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " "(Peer " MACSTR ")", MAC2STR(src_addr)); @@ -1918,7 +2443,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); if (len < 3 + 3) - return -1; + goto error; pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; @@ -1927,21 +2452,26 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (status != 0) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - return -1; + goto error; } pos += 2 /* status code */ + 1 /* dialog token */; ielen = len - (pos - buf); /* start of IE in buf */ + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs piggy-back broken IEs to the end + * of a TDLS Confirm packet, which will fail the link if we don't ignore + * this error. + */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); - return -1; + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround"); } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", (u8 *) kde.lnkid, kde.lnkid_len); @@ -1949,7 +2479,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); - return -1; + goto error; } if (!wpa_tdls_get_privacy(sm)) @@ -1957,7 +2487,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", kde.ftie, sizeof(*ftie)); @@ -1965,7 +2495,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", kde.rsn_ie, kde.rsn_ie_len); @@ -1973,24 +2503,24 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " "with the one sent in TPK M2"); - return -1; + goto error; } if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); - return -1; + goto error; } if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); - return -1; + goto error; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); - return -1; + goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", @@ -2001,25 +2531,46 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M3 (expected %u)", lifetime, peer->lifetime); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - return -1; + goto error; } if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); - return -1; + goto error; } - if (wpa_tdls_set_key(sm, peer) < 0) - return -1; + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } skip_rsn: - wpa_tdls_enable_link(sm, peer); + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_tdls_addset_peer(sm, peer, 0) < 0) + goto error; - return 0; + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M3 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + goto error; + } + } + return ret; +error: + wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + return -1; } @@ -2069,26 +2620,28 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) return -1; } - /* Find existing entry and if found, use that instead of adding - * a new one */ - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) - break; - } + peer = wpa_tdls_add_peer(sm, addr, NULL); + if (peer == NULL) + return -1; - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, addr); - if (peer == NULL) - return -1; + if (peer->tpk_in_progress) { + wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); + return 0; } peer->initiator = 1; /* add the peer to the driver as a "setup in progress" peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, + NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + peer->tpk_in_progress = 1; if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -2096,12 +2649,12 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) } -int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) - return -1; + return; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) @@ -2109,17 +2662,16 @@ int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) } if (peer == NULL || !peer->tpk_success) - return -1; + return; if (sm->tdls_external_setup) { /* * Disable previous link to allow renegotiation to be completed * on AP path. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } - - return wpa_tdls_start(sm, addr); } @@ -2218,7 +2770,8 @@ int wpa_tdls_init(struct wpa_sm *sm) * are assumed to perform everything internally */ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, - &sm->tdls_external_setup) < 0) { + &sm->tdls_external_setup, + &sm->tdls_chan_switch) < 0) { sm->tdls_supported = 1; sm->tdls_external_setup = 0; } @@ -2227,17 +2780,43 @@ int wpa_tdls_init(struct wpa_sm *sm) "driver", sm->tdls_supported ? "" : " not"); wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", sm->tdls_external_setup ? "external" : "internal"); + wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching", + sm->tdls_chan_switch ? "supports" : "does not support"); return 0; } +void wpa_tdls_teardown_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer, *tmp; + + if (!sm) + return; + peer = sm->tdls; + + wpa_printf(MSG_DEBUG, "TDLS: Tear down peers"); + + while (peer) { + tmp = peer->next; + wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR, + MAC2STR(peer->addr)); + if (sm->tdls_external_setup) + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_DEAUTH_LEAVING); + else + wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); + + peer = tmp; + } +} + + static void wpa_tdls_remove_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; peer = sm->tdls; - sm->tdls = NULL; while (peer) { int res; @@ -2246,7 +2825,6 @@ static void wpa_tdls_remove_peers(struct wpa_sm *sm) wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", MAC2STR(peer->addr), res); wpa_tdls_peer_free(sm, peer); - os_free(peer); peer = tmp; } } @@ -2285,39 +2863,61 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -static int wpa_tdls_prohibited(const u8 *ies, size_t len) +static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems) { - struct wpa_eapol_ie_parse elems; + /* bit 38 - TDLS Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x40); +} - if (ies == NULL) - return 0; - if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) - return 0; - - if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) - return 0; - - /* bit 38 - TDLS Prohibited */ - return !!(elems.ext_capab[2 + 4] & 0x40); +static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) +{ + /* bit 39 - TDLS Channel Switch Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x80); } void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); + struct wpa_eapol_ie_parse elems; + + sm->tdls_prohibited = 0; + sm->tdls_chan_switch_prohibited = 0; + + if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return; + + sm->tdls_prohibited = wpa_tdls_prohibited(&elems); wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", sm->tdls_prohibited ? "prohibited" : "allowed"); + sm->tdls_chan_switch_prohibited = + wpa_tdls_chan_switch_prohibited(&elems); + wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS", + sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed"); } void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { + struct wpa_eapol_ie_parse elems; + + if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return; + + if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " "(Re)Association Response IEs"); sm->tdls_prohibited = 1; } + + if (!sm->tdls_chan_switch_prohibited && + wpa_tdls_chan_switch_prohibited(&elems)) { + wpa_printf(MSG_DEBUG, + "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs"); + sm->tdls_chan_switch_prohibited = 1; + } } @@ -2332,3 +2932,78 @@ int wpa_tdls_is_external_setup(struct wpa_sm *sm) { return sm->tdls_external_setup; } + + +int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + struct hostapd_freq_params *freq_params) +{ + struct wpa_tdls_peer *peer; + int ret; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + if (!sm->tdls_chan_switch) { + wpa_printf(MSG_DEBUG, + "TDLS: Channel switching not supported by the driver"); + return -1; + } + + if (sm->tdls_chan_switch_prohibited) { + wpa_printf(MSG_DEBUG, + "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel"); + return -1; + } + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL || !peer->tpk_success) { + wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR + " not found for channel switching", MAC2STR(addr)); + return -1; + } + + if (peer->chan_switch_enabled) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " already has channel switching enabled", + MAC2STR(addr)); + return 0; + } + + ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr, + oper_class, freq_params); + if (!ret) + peer->chan_switch_enabled = 1; + + return ret; +} + + +int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (!peer || !peer->chan_switch_enabled) { + wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for " + MACSTR, MAC2STR(addr)); + return -1; + } + + /* ignore the return value */ + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); + + peer->chan_switch_enabled = 0; + return 0; +} diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index e50404ce78a7..8adeef4af1a5 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,7 @@ * 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) @@ -34,10 +35,12 @@ * @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, +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 @@ -56,14 +59,15 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, } } if (key_mic && - wpa_eapol_key_mic(kck, ver, msg, msg_len, 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 MIC", ver); + "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, 16); - wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); + 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); @@ -84,12 +88,17 @@ out: */ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { - size_t rlen; + 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; + u8 bssid[ETH_ALEN], *rbuf, *key_mic; - if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) + 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; @@ -102,12 +111,16 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) 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, - sizeof(*reply), &rlen, (void *) &reply); + hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; + reply192 = (struct wpa_eapol_key_192 *) reply; - reply->type = sm->proto == WPA_PROTO_RSN ? + 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) @@ -122,15 +135,39 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - WPA_PUT_BE16(reply->key_data_length, 0); + 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, ver, bssid, ETH_P_EAPOL, - rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? - reply->key_mic : NULL); + 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 */ } @@ -158,7 +195,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } if (pmkid && sm->cur_pmksa && - os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { + 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", @@ -194,10 +231,13 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, 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); @@ -231,7 +271,9 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && - !wpa_key_mgmt_ft(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; @@ -273,9 +315,10 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk) { - size_t rlen; + size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - u8 *rbuf; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { @@ -317,19 +360,23 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, 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, sizeof(*reply) + wpa_ie_len, + 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 ? + 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) + 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); @@ -338,47 +385,52 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); - WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); - os_memcpy(reply + 1, wpa_ie, wpa_ie_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, ver, dst, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + 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) + const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) - return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); + return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ - wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", - sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, - (u8 *) ptk, ptk_len, - wpa_key_mgmt_sha256(sm->key_mgmt)); - return 0; + 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; - u8 buf[8]; 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 " @@ -392,20 +444,17 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memset(&ie, 0, sizeof(ie)); -#ifndef CONFIG_NO_WPA2 - if (sm->proto == WPA_PROTO_RSN) { + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ - const u8 *_buf = (const u8 *) (key + 1); - size_t len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); - if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) + 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); } } -#endif /* CONFIG_NO_WPA2 */ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { @@ -431,21 +480,49 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, * been verified when processing message 3/4. */ ptk = &sm->tptk; wpa_derive_ptk(sm, src_addr, key, ptk); - /* Supplicant: swap tx/rx Mic keys */ - os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); - os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); - os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); + 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, - sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, - ptk)) + 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); } @@ -537,7 +614,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, keylen = wpa_cipher_key_len(sm->pairwise_cipher); rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); - if (sm->proto == WPA_PROTO_RSN) { + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; @@ -545,7 +622,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, - (u8 *) sm->ptk.tk1, keylen) < 0) { + 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 ")", @@ -553,6 +630,9 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, 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, @@ -625,6 +705,7 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, 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, @@ -634,8 +715,10 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, "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; } @@ -664,7 +747,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const u8 *gtk, size_t gtk_len, int key_info) { -#ifndef CONFIG_NO_WPA2 struct wpa_gtk_data gd; /* @@ -691,21 +773,21 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; - if (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)) { + 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; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -713,13 +795,15 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { #ifdef CONFIG_IEEE80211W - if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + 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; - if (ie->igtk_len != sizeof(*igtk)) + 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); @@ -727,15 +811,16 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, "pn %02x%02x%02x%02x%02x%02x", keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", - igtk->igtk, WPA_IGTK_LEN); + 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_ALG_IGTK, broadcast_ether_addr, + 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, WPA_IGTK_LEN) < 0) { + igtk->igtk, len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to configure IGTK to the driver"); return -1; @@ -868,7 +953,8 @@ static int ft_validate_rsnie(struct wpa_sm *sm, return -1; } - if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { + 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"); @@ -988,49 +1074,49 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info - * @kde: KDEs to include the EAPOL-Key frame - * @kde_len: Length of KDEs * @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, - const u8 *kde, size_t kde_len, struct wpa_ptk *ptk) { - size_t rlen; + size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - u8 *rbuf; - - if (kde) - wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); + 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, - sizeof(*reply) + kde_len, - &rlen, (void *) &reply); + hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; - reply->type = sm->proto == WPA_PROTO_RSN ? + 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) + 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_PUT_BE16(reply->key_data_length, kde_len); - if (kde) - os_memcpy(reply + 1, kde, kde_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, ver, dst, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, + rbuf, rlen, key_mic); return 0; } @@ -1038,10 +1124,10 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, - u16 ver) + u16 ver, const u8 *key_data, + size_t key_data_len) { - u16 key_info, keylen, len; - const u8 *pos; + u16 key_info, keylen; struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); @@ -1050,10 +1136,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, key_info = WPA_GET_BE16(key->key_info); - pos = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); - if (wpa_supplicant_parse_ies(pos, len, &ie) < 0) + 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, @@ -1067,7 +1151,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } - if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { + 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); @@ -1095,8 +1182,16 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, 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, - NULL, 0, &sm->ptk)) { + &sm->ptk)) { goto failed; } @@ -1118,7 +1213,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); - if (ie.gtk && + 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, @@ -1132,8 +1230,21 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } - wpa_sm_set_rekey_offload(sm); + 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: @@ -1193,22 +1304,15 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, const struct wpa_eapol_key *key, - size_t keydatalen, int key_info, - size_t extra_len, u16 ver, - struct wpa_gtk_data *gd) + const u8 *key_data, + size_t key_data_len, u16 key_info, + u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; - u8 ek[32]; + u16 gtk_len; - gd->gtk_len = WPA_GET_BE16(key->key_length); - maxkeylen = keydatalen; - if (keydatalen > extra_len) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "WPA: Truncated EAPOL-Key packet: " - "key_data_length=%lu > extra_len=%lu", - (unsigned long) keydatalen, (unsigned long) extra_len); - return -1; - } + 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, @@ -1219,45 +1323,50 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, maxkeylen -= 8; } - if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, - gd->gtk_len, maxkeylen, + 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) { - os_memcpy(ek, key->key_iv, 16); - os_memcpy(ek + 16, sm->ptk.kek, 16); - if (keydatalen > sizeof(gd->gtk)) { + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { + 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) keydatalen); + (unsigned long) key_data_len); return -1; } - os_memcpy(gd->gtk, key + 1, keydatalen); - if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { + 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)); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - if (keydatalen % 8) { + if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %lu", - (unsigned long) keydatalen); + (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) keydatalen, + (unsigned long) key_data_len, (unsigned long) maxkeylen); return -1; } - if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, - (const u8 *) (key + 1), gd->gtk)) { + 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"); @@ -1278,32 +1387,41 @@ 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 rlen; + size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - u8 *rbuf; + 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, - sizeof(*reply), &rlen, (void *) &reply); + hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; - reply->type = sm->proto == WPA_PROTO_RSN ? + 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) + 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_PUT_BE16(reply->key_data_length, 0); + 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, ver, sm->bssid, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, + ETH_P_EAPOL, rbuf, rlen, key_mic); return 0; } @@ -1312,12 +1430,19 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, - int extra_len, u16 ver) + const u8 *key_data, + size_t key_data_len, u16 ver) { - u16 key_info, keydatalen; + 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; @@ -1325,17 +1450,15 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); - keydatalen = WPA_GET_BE16(key->key_data_length); - if (sm->proto == WPA_PROTO_RSN) { - ret = wpa_supplicant_process_1_of_2_rsn(sm, - (const u8 *) (key + 1), - keydatalen, 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, keydatalen, - key_info, extra_len, - ver, &gd); + 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); @@ -1346,6 +1469,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, 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_INFO, "WPA: Group rekeying " @@ -1353,34 +1477,37 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); - - wpa_sm_set_rekey_offload(sm); } 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 *key, + struct wpa_eapol_key_192 *key, u16 ver, const u8 *buf, size_t len) { - u8 mic[16]; + 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, 16); + os_memcpy(mic, key->key_mic, mic_len); if (sm->tptk_set) { - os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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"); @@ -1389,14 +1516,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, 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, 16); - wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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"); @@ -1421,12 +1549,11 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, /* 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) + struct wpa_eapol_key *key, u16 ver, + u8 *key_data, size_t *key_data_len) { - u16 keydatalen = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", - (u8 *) (key + 1), keydatalen); + 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 " @@ -1436,49 +1563,53 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, /* 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) { + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { u8 ek[32]; os_memcpy(ek, key->key_iv, 16); - os_memcpy(ek + 16, sm->ptk.kek, 16); - if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { + 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)); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + 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 (keydatalen % 8) { + if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Unsupported AES-WRAP len %d", - keydatalen); + "WPA: Unsupported AES-WRAP len %u", + (unsigned int) *key_data_len); return -1; } - keydatalen -= 8; /* AES-WRAP adds 8 bytes */ - buf = os_malloc(keydatalen); + *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, keydatalen / 8, - (u8 *) (key + 1), buf)) { + 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 + 1, buf, keydatalen); + os_memcpy(key_data, buf, *key_data_len); os_free(buf); - WPA_PUT_BE16(key->key_data_length, keydatalen); + 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", - (u8 *) (key + 1), keydatalen); + key_data, *key_data_len); return 0; } @@ -1498,7 +1629,9 @@ void wpa_sm_aborted_cached(struct wpa_sm *sm) static void wpa_eapol_key_dump(struct wpa_sm *sm, - const struct wpa_eapol_key *key) + 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); @@ -1520,15 +1653,14 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm, 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), - WPA_GET_BE16(key->key_data_length)); + 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", key->key_mic, 16); + wpa_hexdump(MSG_DEBUG, " key_mic", mic, mic_len); #endif /* CONFIG_NO_STDOUT_DEBUG */ } @@ -1552,34 +1684,34 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm, int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { - size_t plen, data_len, extra_len; - struct ieee802_1x_hdr *hdr; + 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; + 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 */ - if (len < sizeof(*hdr) + sizeof(*key)) { + 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) + sizeof(*key)); + (unsigned long) sizeof(*hdr) + keyhdrlen); return 0; } - tmp = os_malloc(len); - if (tmp == NULL) - return -1; - os_memcpy(tmp, buf, len); - - hdr = (struct ieee802_1x_hdr *) tmp; - key = (struct wpa_eapol_key *) (hdr + 1); + 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, @@ -1596,7 +1728,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 0; goto out; } - if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { + 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)", @@ -1604,6 +1737,27 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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) { @@ -1613,28 +1767,53 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 0; goto out; } - wpa_eapol_key_dump(sm, key); - eapol_sm_notify_lower_layer_success(sm->eapol, 0); - wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); - 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); + 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) { + 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). */ @@ -1647,7 +1826,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { - if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + 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"); @@ -1656,6 +1837,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } 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 " @@ -1669,11 +1851,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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; - } - if (sm->pairwise_cipher == WPA_CIPHER_GCMP && - ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + } 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); @@ -1743,31 +1928,21 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && - wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) + 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, key, ver, tmp, data_len)) + peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, + data_len)) goto out; #endif /* CONFIG_PEERKEY */ - extra_len = data_len - sizeof(*hdr) - sizeof(*key); - - if (WPA_GET_BE16(key->key_data_length) > extra_len) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " - "frame - key_data overflow (%d > %lu)", - WPA_GET_BE16(key->key_data_length), - (unsigned long) extra_len); - goto out; - } - extra_len = WPA_GET_BE16(key->key_data_length); - - if (sm->proto == WPA_PROTO_RSN && + 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)) + if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, + &key_data_len)) goto out; - extra_len = WPA_GET_BE16(key->key_data_length); } if (key_info & WPA_KEY_INFO_KEY_TYPE) { @@ -1779,24 +1954,28 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } if (peerkey) { /* PeerKey 4-Way Handshake */ - peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver); + 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); + 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); + 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, extra_len, key_info, + 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, - extra_len, ver); + key_data, key_data_len, + ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key (Group) without Mic bit - " @@ -1807,7 +1986,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 1; out: - os_free(tmp); + bin_clear_free(tmp, data_len); return ret; } @@ -1817,7 +1996,8 @@ 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 ? + 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: @@ -1842,6 +2022,10 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) 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; } @@ -1899,7 +2083,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; len = ret; @@ -1926,7 +2110,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); - if (ret >= 0 && (size_t) ret < buflen) + if (!os_snprintf_error(buflen - len, ret)) len += ret; return (int) len; @@ -2024,6 +2208,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) 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 @@ -2080,12 +2265,18 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) */ 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 */ } @@ -2098,6 +2289,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) */ 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) @@ -2105,6 +2299,11 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #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; } @@ -2113,10 +2312,12 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) * @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) +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *bssid) { if (sm == NULL) return; @@ -2129,6 +2330,12 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) 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); + } } @@ -2208,6 +2415,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) } 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; @@ -2217,6 +2425,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; + sm->p2p = 0; } } @@ -2326,44 +2535,6 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, } -/** - * wpa_sm_get_param - Get WPA state machine parameters - * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @param: Parameter field - * Returns: Parameter value - */ -unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) -{ - if (sm == NULL) - return 0; - - switch (param) { - case RSNA_PMK_LIFETIME: - return sm->dot11RSNAConfigPMKLifetime; - case RSNA_PMK_REAUTH_THRESHOLD: - return sm->dot11RSNAConfigPMKReauthThreshold; - case RSNA_SA_TIMEOUT: - return sm->dot11RSNAConfigSATimeout; - case WPA_PARAM_PROTO: - return sm->proto; - case WPA_PARAM_PAIRWISE: - return sm->pairwise_cipher; - case WPA_PARAM_GROUP: - return sm->group_cipher; - case WPA_PARAM_KEY_MGMT: - return sm->key_mgmt; -#ifdef CONFIG_IEEE80211W - case WPA_PARAM_MGMT_GROUP: - return sm->mgmt_group_cipher; -#endif /* CONFIG_IEEE80211W */ - case WPA_PARAM_RSN_ENABLED: - return sm->rsn_enabled; - default: - return 0; - } -} - - /** * wpa_sm_get_status - Get WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -2389,7 +2560,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher), wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2402,7 +2573,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, ret = os_snprintf(pos, end - pos, "pmf=%d\n", (rsn.capabilities & WPA_CAPABILITY_MFPR) ? 2 : 1); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -2412,6 +2583,21 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, } +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() @@ -2586,11 +2772,7 @@ 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) { -#ifndef CONFIG_NO_WPA2 return pmksa_cache_list(sm->pmksa, buf, len); -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -2602,6 +2784,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) 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 */ } @@ -2621,38 +2808,29 @@ 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) { -#ifndef CONFIG_NO_WPA2 - pmksa_cache_flush(sm->pmksa, network_ctx); -#endif /* CONFIG_NO_WPA2 */ + 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) { - struct wpa_gtk_data gd; -#ifdef CONFIG_IEEE80211W - struct wpa_igtk_kde igd; - u16 keyidx; -#endif /* CONFIG_IEEE80211W */ u16 keyinfo; u8 keylen; /* plaintext key len */ u8 *key_rsc; - os_memset(&gd, 0, sizeof(gd)); -#ifdef CONFIG_IEEE80211W - os_memset(&igd, 0, sizeof(igd)); -#endif /* CONFIG_IEEE80211W */ - - 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; - } - 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; @@ -2670,27 +2848,37 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) 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, WPA_IGTK_LEN); + os_memcpy(igd.igtk, buf + 10, keylen); wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", - igd.igtk, WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, + 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, WPA_IGTK_LEN) < 0) { + 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"); @@ -2700,3 +2888,67 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) 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/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 791974c2b303..e163b7010b70 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA definitions - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -12,10 +12,12 @@ #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 */ @@ -50,17 +52,31 @@ struct wpa_sm_ctx { 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_ext_setup, int *tdls_chan_switch); int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len); + 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, + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, - size_t supp_rates_len); + 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, const u8 *kck, + 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); }; @@ -87,6 +103,7 @@ struct rsn_supp_config { const u8 *ssid; size_t ssid_len; int wpa_ptk_rekey; + int p2p; }; #ifndef CONFIG_NO_WPA @@ -95,7 +112,8 @@ 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); +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); @@ -113,11 +131,10 @@ 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); -unsigned int wpa_sm_get_param(struct wpa_sm *sm, - enum wpa_sm_conf_params param); 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); @@ -136,6 +153,13 @@ 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) @@ -227,14 +251,13 @@ static inline int wpa_sm_set_param(struct wpa_sm *sm, return -1; } -static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm, - enum wpa_sm_conf_params param) +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_get_status(struct wpa_sm *sm, char *buf, - size_t buflen, int verbose) +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) { return 0; } @@ -291,15 +314,33 @@ static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, { } +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 @@ -310,6 +351,7 @@ 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, @@ -341,6 +383,10 @@ 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) @@ -355,15 +401,20 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, 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); -int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr); -int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +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_link(struct wpa_sm *sm, const u8 *addr); +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); diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index 2df060ca87eb..06dea0550f1b 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,8 +19,7 @@ #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, size_t ptk_len) + const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; @@ -43,13 +42,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, 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); - wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, - sm->bssid, sm->pmk_r1_name, - (u8 *) ptk, ptk_len, ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); - wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); - - return 0; + 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); } @@ -134,6 +129,7 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) * @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 @@ -144,7 +140,8 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) */ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, const u8 *pmk_name, - const u8 *kck, const u8 *target_ap, + const u8 *kck, size_t kck_len, + const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len, const u8 *ap_mdie) { @@ -207,6 +204,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, 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); @@ -296,7 +295,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* Information element count */ ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); - if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, + 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, @@ -331,7 +330,7 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) 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.tk1, keylen) < 0) { + sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); return -1; } @@ -358,7 +357,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, sm->bssid, NULL, 0, mdie); + NULL, 0, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); @@ -374,7 +373,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; - size_t ft_ies_len, ptk_len; + size_t ft_ies_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; @@ -400,8 +399,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } } - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + 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; @@ -441,7 +439,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } if (parse.r0kh_id_len != sm->r0kh_id_len || - os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + 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", @@ -457,7 +456,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } if (parse.rsn_pmkid == NULL || - os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { + 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; @@ -475,16 +475,14 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; - wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, - bssid, sm->pmk_r1_name, - (u8 *) &sm->ptk, ptk_len, ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", - (u8 *) &sm->ptk, ptk_len); - wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + 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, bssid, + 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) { @@ -526,14 +524,20 @@ int wpa_ft_is_completed(struct wpa_sm *sm) if (sm == NULL) return 0; - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) + 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) { @@ -557,7 +561,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } gtk_len = gtk_elem_len - 19; - if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { + 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; @@ -589,6 +594,13 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, } 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 " @@ -629,7 +641,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, return -1; } - if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { + 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; @@ -660,12 +673,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, struct rsn_mdie *mdie; struct rsn_ftie *ftie; unsigned int count; - u8 mic[16]; + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + 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; @@ -714,7 +726,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, } if (parse.r0kh_id_len != sm->r0kh_id_len || - os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + 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", @@ -729,14 +742,15 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { + 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(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + 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; @@ -752,7 +766,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, + 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, @@ -762,7 +776,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (os_memcmp(mic, ftie->mic, 16) != 0) { + 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); @@ -788,9 +802,12 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, if (parse.ric) { wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", parse.ric, parse.ric_len); - /* TODO: parse response and inform driver about results */ + /* TODO: parse response and inform driver about results when + * using wpa_supplicant SME */ } + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + return 0; } @@ -818,7 +835,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, target_ap, NULL, 0, mdie); + 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); diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 9f9e641c3877..965a9c1d577c 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -1,6 +1,6 @@ /* * Internal WPA/RSN supplicant state machine definitions - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +23,7 @@ struct wpa_sm { 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; @@ -58,6 +59,7 @@ struct wpa_sm { u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; + int p2p; u8 own_addr[ETH_ALEN]; const char *ifname; @@ -91,6 +93,7 @@ struct wpa_sm { #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; + int tdls_chan_switch_prohibited; int tdls_disabled; /* The driver supports TDLS */ @@ -101,6 +104,9 @@ struct wpa_sm { * 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 @@ -122,6 +128,10 @@ struct wpa_sm { 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 */ }; @@ -245,30 +255,34 @@ 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.kck, sm->rx_replay_counter); + 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_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_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, const u8 *buf, + 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, - buf, len); + peer_capab, initiator, buf, + len); return -1; } @@ -282,18 +296,60 @@ static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, static inline int wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, - u16 capability, const u8 *supp_rates, - size_t supp_rates_len) + 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, - capability, supp_rates, - supp_rates_len); + 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 */ -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, +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, @@ -304,12 +360,10 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, 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, - const u8 *kde, size_t kde_len, 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, size_t ptk_len); + 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); diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 3d7536595773..cb334df675be 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA/RSN IE and KDE processing - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -107,7 +107,6 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, int key_mgmt, int mgmt_group_cipher, struct wpa_sm *sm) { -#ifndef CONFIG_NO_WPA2 u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; @@ -174,6 +173,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } 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); @@ -202,7 +205,7 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } #ifdef CONFIG_IEEE80211W - if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { if (!sm->cur_pmksa) { /* PMKID Count */ WPA_PUT_LE16(pos, 0); @@ -210,7 +213,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } /* Management Group Cipher Suite */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + mgmt_group_cipher)); pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ @@ -220,12 +224,67 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); return pos - rsn_ie; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } +#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() @@ -241,6 +300,13 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) 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, @@ -249,6 +315,42 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) } +/** + * 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 @@ -349,6 +451,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #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; } @@ -427,6 +548,31 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, } 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) { + ie->ht_capabilities = pos + 2; + ie->ht_capabilities_len = pos[1]; + } 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) { + ie->vht_capabilities = pos + 2; + ie->vht_capabilities_len = pos[1]; + } 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) @@ -435,6 +581,14 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, 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]); diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 5afdfe9fc725..0fc42cc492ac 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -49,6 +49,22 @@ struct wpa_eapol_ie_parse { size_t supp_rates_len; const u8 *ext_supp_rates; size_t ext_supp_rates_len; + const u8 *ht_capabilities; + size_t ht_capabilities_len; + const u8 *vht_capabilities; + size_t vht_capabilities_len; + 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, diff --git a/src/tls/asn1.c b/src/tls/asn1.c index 53acd530d917..cec109292d5a 100644 --- a/src/tls/asn1.c +++ b/src/tls/asn1.c @@ -1,6 +1,6 @@ /* * ASN.1 DER parsing - * Copyright (c) 2006, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,17 @@ #include "common.h" #include "asn1.h" +struct asn1_oid asn1_sha1_oid = { + .oid = { 1, 3, 14, 3, 2, 26 }, + .len = 6 +}; + +struct asn1_oid asn1_sha256_oid = { + .oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 }, + .len = 9 +}; + + int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) { const u8 *pos, *end; @@ -140,7 +151,7 @@ int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, } -void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) +void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len) { char *pos = buf; size_t i; @@ -155,7 +166,7 @@ void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, "%s%lu", i == 0 ? "" : ".", oid->oid[i]); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) break; pos += ret; } @@ -204,3 +215,19 @@ unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) return val; } + + +int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b) +{ + size_t i; + + if (a->len != b->len) + return 0; + + for (i = 0; i < a->len; i++) { + if (a->oid[i] != b->oid[i]) + return 0; + } + + return 1; +} diff --git a/src/tls/asn1.h b/src/tls/asn1.h index 6342c4cc79fd..74750076731d 100644 --- a/src/tls/asn1.h +++ b/src/tls/asn1.h @@ -60,7 +60,11 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, const u8 **next); -void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); +void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len); unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); +int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b); + +extern struct asn1_oid asn1_sha1_oid; +extern struct asn1_oid asn1_sha256_oid; #endif /* ASN1_H */ diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 741b442ca912..3fb8fbed25e7 100644 --- a/src/tls/libtommath.c +++ b/src/tls/libtommath.c @@ -42,6 +42,9 @@ /* Include faster sqr at the cost of about 0.5 kB in code */ #define BN_FAST_S_MP_SQR_C +/* About 0.25 kB of code, but ~1.7kB of stack space! */ +#define BN_FAST_S_MP_MUL_DIGS_C + #else /* LTM_FAST */ #define BN_MP_DIV_SMALL @@ -139,7 +142,9 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); static int s_mp_sqr(mp_int * a, mp_int * b); static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); +#ifdef BN_FAST_S_MP_MUL_DIGS_C static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +#endif #ifdef BN_MP_INIT_MULTI_C static int mp_init_multi(mp_int *mp, ...); @@ -671,6 +676,9 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) #ifdef BN_MP_EXPTMOD_FAST_C } #endif + if (dr == 0) { + /* avoid compiler warnings about possibly unused variable */ + } } @@ -2339,12 +2347,14 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) mp_word r; mp_digit tmpx, *tmpt, *tmpy; +#ifdef BN_FAST_S_MP_MUL_DIGS_C /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_digs (a, b, c, digs); } +#endif if ((res = mp_init_size (&t, digs)) != MP_OKAY) { return res; @@ -2397,6 +2407,7 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) } +#ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * * This is the fast column-array [comba] multiplier. It is @@ -2482,6 +2493,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) mp_clamp (c); return MP_OKAY; } +#endif /* BN_FAST_S_MP_MUL_DIGS_C */ /* init an mp_init for a given size */ diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c index b6fde5ee868a..141ac50df401 100644 --- a/src/tls/pkcs1.c +++ b/src/tls/pkcs1.c @@ -1,6 +1,6 @@ /* * PKCS #1 (RSA Encryption) - * Copyright (c) 2006-2009, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,7 +9,9 @@ #include "includes.h" #include "common.h" +#include "crypto/crypto.h" #include "rsa.h" +#include "asn1.h" #include "pkcs1.h" @@ -113,6 +115,11 @@ int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, pos++; if (pos == end) return -1; + if (pos - out - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding"); + return -1; + } pos++; *outlen -= pos - out; @@ -142,35 +149,26 @@ int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, * BT = 00 or 01 * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) * k = length of modulus in octets + * + * Based on 10.1.3, "The block type shall be 01" for a signature. */ if (len < 3 + 8 + 16 /* min hash len */ || - plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) { + plain[0] != 0x00 || plain[1] != 0x01) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure"); return -1; } pos = plain + 3; - if (plain[1] == 0x00) { - /* BT = 00 */ - if (plain[2] != 0x00) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " - "PS (BT=00)"); - return -1; - } - while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00) - pos++; - } else { - /* BT = 01 */ - if (plain[2] != 0xff) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " - "PS (BT=01)"); - return -1; - } - while (pos < plain + len && *pos == 0xff) - pos++; + /* BT = 01 */ + if (plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=01)"); + return -1; } + while (pos < plain + len && *pos == 0xff) + pos++; if (pos - plain - 2 < 8) { /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ @@ -193,3 +191,130 @@ int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, return 0; } + + +int pkcs1_v15_sig_ver(struct crypto_public_key *pk, + const u8 *s, size_t s_len, + const struct asn1_oid *hash_alg, + const u8 *hash, size_t hash_len) +{ + int res; + u8 *decrypted; + size_t decrypted_len; + const u8 *pos, *end, *next, *da_end; + struct asn1_hdr hdr; + struct asn1_oid oid; + + decrypted = os_malloc(s_len); + if (decrypted == NULL) + return -1; + decrypted_len = s_len; + res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted, + &decrypted_len); + if (res < 0) { + wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed"); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len); + + /* + * PKCS #1 v1.5, 10.1.2: + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest + * } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + * + */ + if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #1: Expected SEQUENCE (DigestInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(decrypted); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* + * X.509: + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #1: Expected SEQUENCE (AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(decrypted); + return -1; + } + da_end = hdr.payload + hdr.length; + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { + wpa_printf(MSG_DEBUG, + "PKCS #1: Failed to parse digestAlgorithm"); + os_free(decrypted); + return -1; + } + + if (!asn1_oid_equal(&oid, hash_alg)) { + char txt[100], txt2[100]; + asn1_oid_to_str(&oid, txt, sizeof(txt)); + asn1_oid_to_str(hash_alg, txt2, sizeof(txt2)); + wpa_printf(MSG_DEBUG, + "PKCS #1: Hash alg OID mismatch: was %s, expected %s", + txt, txt2); + os_free(decrypted); + return -1; + } + + /* Digest ::= OCTET STRING */ + pos = da_end; + end = decrypted + decrypted_len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #1: Expected OCTETSTRING (Digest) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest", + hdr.payload, hdr.length); + + if (hdr.length != hash_len || + os_memcmp_const(hdr.payload, hash, hdr.length) != 0) { + wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash"); + os_free(decrypted); + return -1; + } + + os_free(decrypted); + + if (hdr.payload + hdr.length != end) { + wpa_printf(MSG_INFO, + "PKCS #1: Extra data after signature - reject"); + + wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data", + hdr.payload + hdr.length, + end - hdr.payload - hdr.length); + return -1; + } + + return 0; +} diff --git a/src/tls/pkcs1.h b/src/tls/pkcs1.h index ed64defaafe0..f37ebf3875ea 100644 --- a/src/tls/pkcs1.h +++ b/src/tls/pkcs1.h @@ -9,6 +9,9 @@ #ifndef PKCS1_H #define PKCS1_H +struct crypto_public_key; +struct asn1_oid; + int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, int use_private, const u8 *in, size_t inlen, u8 *out, size_t *outlen); @@ -18,5 +21,9 @@ int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); +int pkcs1_v15_sig_ver(struct crypto_public_key *pk, + const u8 *s, size_t s_len, + const struct asn1_oid *hash_alg, + const u8 *hash, size_t hash_len); #endif /* PKCS1_H */ diff --git a/src/tls/rsa.c b/src/tls/rsa.c index 125c4205b705..0b7b530bc37c 100644 --- a/src/tls/rsa.c +++ b/src/tls/rsa.c @@ -1,6 +1,6 @@ /* * RSA - * Copyright (c) 2006, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -116,6 +116,29 @@ error: } +struct crypto_rsa_key * +crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len, + const u8 *e, size_t e_len) +{ + struct crypto_rsa_key *key; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->n = bignum_init(); + key->e = bignum_init(); + if (key->n == NULL || key->e == NULL || + bignum_set_unsigned_bin(key->n, n, n_len) < 0 || + bignum_set_unsigned_bin(key->e, e, e_len) < 0) { + crypto_rsa_free(key); + return NULL; + } + + return key; +} + + /** * crypto_rsa_import_private_key - Import an RSA private key * @buf: Key buffer (DER encoded RSA private key) diff --git a/src/tls/rsa.h b/src/tls/rsa.h index c236a9df4449..b65818ee1546 100644 --- a/src/tls/rsa.h +++ b/src/tls/rsa.h @@ -14,6 +14,9 @@ struct crypto_rsa_key; struct crypto_rsa_key * crypto_rsa_import_public_key(const u8 *buf, size_t len); struct crypto_rsa_key * +crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len, + const u8 *e, size_t e_len); +struct crypto_rsa_key * crypto_rsa_import_private_key(const u8 *buf, size_t len); size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key); int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 12148b61ddfc..facdd659173d 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -459,10 +459,15 @@ struct tlsv1_client * tlsv1_client_init(void) count = 0; suites = conn->cipher_suites; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; @@ -565,8 +570,26 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; - case TLS_DH_anon_WITH_AES_128_CBC_SHA256: - cipher = "ADH-AES-128-SHA256"; + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; @@ -574,15 +597,30 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - cipher = "AES-256-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; break; case TLS_RSA_WITH_AES_128_CBC_SHA256: cipher = "AES-128-SHA256"; break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; + break; default: return -1; } diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 3269ecf668ee..9ce96803753a 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -409,10 +409,38 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, } +static unsigned int count_bits(const u8 *val, size_t len) +{ + size_t i; + unsigned int bits; + u8 tmp; + + for (i = 0; i < len; i++) { + if (val[i]) + break; + } + if (i == len) + return 0; + + bits = (len - i - 1) * 8; + tmp = val[i]; + while (tmp) { + bits++; + tmp >>= 1; + } + + return bits; +} + + static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, - const u8 *buf, size_t len) + const u8 *buf, size_t len, + tls_key_exchange key_exchange) { - const u8 *pos, *end; + const u8 *pos, *end, *server_params, *server_params_end; + u8 alert; + unsigned int bits; + u16 val; tlsv1_client_free_dh(conn); @@ -421,11 +449,20 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; - conn->dh_p_len = WPA_GET_BE16(pos); + server_params = pos; + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", - (unsigned long) conn->dh_p_len); + if (val == 0 || val > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val); + goto fail; + } + conn->dh_p_len = val; + bits = count_bits(pos, conn->dh_p_len); + if (bits < 768) { + wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)", + bits); + wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime", + pos, conn->dh_p_len); goto fail; } conn->dh_p = os_malloc(conn->dh_p_len); @@ -438,10 +475,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; - conn->dh_g_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) + if (val == 0 || val > (size_t) (end - pos)) goto fail; + conn->dh_g_len = val; conn->dh_g = os_malloc(conn->dh_g_len); if (conn->dh_g == NULL) goto fail; @@ -454,10 +492,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; - conn->dh_ys_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) + if (val == 0 || val > (size_t) (end - pos)) goto fail; + conn->dh_ys_len = val; conn->dh_ys = os_malloc(conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; @@ -465,6 +504,59 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); + server_params_end = pos; + + if (key_exchange == TLS_KEY_X_DHE_RSA) { + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + int hlen; + + if (conn->rl.tls_version == TLS_VERSION_1_2) { +#ifdef CONFIG_TLSV12 + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used + * signature and hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) + goto fail; + if (pos[0] != TLS_HASH_ALG_SHA256 || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", + pos[0], pos[1]); + goto fail; + } + pos += 2; + + hlen = tlsv12_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + server_params_end - server_params, hash); +#else /* CONFIG_TLSV12 */ + goto fail; +#endif /* CONFIG_TLSV12 */ + } else { + hlen = tls_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + server_params_end - server_params, hash); + } + + if (hlen < 0) + goto fail; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash", + hash, hlen); + + if (tls_verify_signature(conn->rl.tls_version, + conn->server_rsa_key, + hash, hlen, pos, end - pos, + &alert) < 0) + goto fail; + } return 0; @@ -543,8 +635,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); suite = tls_get_cipher_suite(conn->rl.cipher_suite); - if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { - if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { + if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon || + suite->key_exchange == TLS_KEY_X_DHE_RSA)) { + if (tlsv1_process_diffie_hellman(conn, pos, len, + suite->key_exchange) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -871,8 +965,10 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data, TLS_VERIFY_DATA_LEN); - if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); return -1; } diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index d789efb4255e..d192f44f4088 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -1,6 +1,6 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -205,7 +205,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, } -static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) +static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; @@ -399,8 +399,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn, hs_length = pos; pos += 3; /* body - ClientKeyExchange */ - if (keyx == TLS_KEY_X_DH_anon) { - if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) + if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) { + if (tlsv1_key_x_dh(conn, &pos, end) < 0) return -1; } else { if (tlsv1_key_x_rsa(conn, &pos, end) < 0) @@ -432,7 +432,6 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; u8 hash[100], *hpos; - enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; @@ -505,21 +504,17 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, } else { #endif /* CONFIG_TLSV12 */ - if (alg == SIGN_ALG_RSA) { - hlen = MD5_MAC_LEN; - if (conn->verify.md5_cert == NULL || - crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) - { - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - conn->verify.md5_cert = NULL; - crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); - conn->verify.sha1_cert = NULL; - return -1; - } - hpos += MD5_MAC_LEN; - } else - crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; @@ -532,8 +527,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, } conn->verify.sha1_cert = NULL; - if (alg == SIGN_ALG_RSA) - hlen += MD5_MAC_LEN; + hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c index d21286283b83..dabc12a12978 100644 --- a/src/tls/tlsv1_common.c +++ b/src/tls/tlsv1_common.c @@ -1,6 +1,6 @@ /* * TLSv1 common routines - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "x509v3.h" @@ -33,6 +34,10 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { TLS_HASH_SHA }, { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC, + TLS_HASH_SHA}, + { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, @@ -41,24 +46,31 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } }; -#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) -#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites) +#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites) static const struct tls_cipher_data tls_ciphers[] = { @@ -84,7 +96,7 @@ static const struct tls_cipher_data tls_ciphers[] = { CRYPTO_CIPHER_ALG_AES } }; -#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers) +#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers) /** @@ -320,3 +332,161 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, outlen); } + + +#ifdef CONFIG_TLSV12 +int tlsv12_key_x_server_params_hash(u16 tls_version, + const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash) +{ + size_t hlen; + struct crypto_hash *ctx; + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = SHA256_MAC_LEN; + if (crypto_hash_finish(ctx, hash, &hlen) < 0) + return -1; + + return hlen; +} +#endif /* CONFIG_TLSV12 */ + + +int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash) +{ + u8 *hpos; + size_t hlen; + struct crypto_hash *ctx; + + hpos = hash; + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = MD5_MAC_LEN; + if (crypto_hash_finish(ctx, hash, &hlen) < 0) + return -1; + hpos += hlen; + + ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + if (ctx == NULL) + return -1; + crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); + crypto_hash_update(ctx, server_params, server_params_len); + hlen = hash + sizeof(hash) - hpos; + if (crypto_hash_finish(ctx, hpos, &hlen) < 0) + return -1; + hpos += hlen; + return hpos - hash; +} + + +int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, + const u8 *data, size_t data_len, + const u8 *pos, size_t len, u8 *alert) +{ + u8 *buf; + const u8 *end = pos + len; + const u8 *decrypted; + u16 slen; + size_t buflen; + + if (end - pos < 2) { + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + slen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < slen) { + *alert = TLS_ALERT_DECODE_ERROR; + return -1; + } + if (end - pos > slen) { + wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature", + pos + slen, end - pos - slen); + end = pos + slen; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); + if (pk == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + buflen = end - pos; + buf = os_malloc(end - pos); + if (buf == NULL) { + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) < + 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + decrypted = buf; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", + decrypted, buflen); + +#ifdef CONFIG_TLSV12 + if (tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); + decrypted = buf + 19; + buflen -= 19; + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + + if (buflen != data_len || + os_memcmp_const(decrypted, data, data_len) != 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash"); + os_free(buf); + *alert = TLS_ALERT_DECRYPT_ERROR; + return -1; + } + + os_free(buf); + + return 0; +} diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index f28c0cdc4790..26e68af16606 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -1,6 +1,6 @@ /* * TLSv1 common definitions - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -257,5 +257,16 @@ int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash); +int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random, + const u8 *server_random, + const u8 *server_params, + size_t server_params_len, u8 *hash); +int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, + const u8 *data, size_t data_len, + const u8 *pos, size_t len, u8 *alert); #endif /* TLSV1_COMMON_H */ diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c index 3bec3be36f07..0c6897a8fc23 100644 --- a/src/tls/tlsv1_record.c +++ b/src/tls/tlsv1_record.c @@ -456,7 +456,7 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, return -1; } if (hlen != rl->hash_size || - os_memcmp(hash, out_data + plen, hlen) != 0 || + os_memcmp_const(hash, out_data + plen, hlen) != 0 || force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " "received message (force_mac_error=%d)", diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 2880309ebf51..93ae4888d898 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,6 +21,31 @@ */ +void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + wpa_printf(MSG_DEBUG, "TLSv1: %s", buf); + if (conn->log_cb) + conn->log_cb(conn->log_cb_ctx, buf); + + os_free(buf); +} + + void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) { conn->alert_level = level; @@ -250,8 +275,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " - "failed"); + tlsv1_server_log(conn, "Record layer processing failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } @@ -265,14 +289,13 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Alert " - "underflow"); + tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", - out_pos[0], out_pos[1]); + tlsv1_server_log(conn, "Received alert %d:%d", + out_pos[0], out_pos[1]); if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -285,13 +308,23 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " - "0x%x", pos[0]); + tlsv1_server_log(conn, "Unexpected content type 0x%x", + pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH | + TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake"); + conn->test_failure_reported = 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " @@ -361,8 +394,15 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) count = 0; suites = conn->cipher_suites; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; @@ -476,14 +516,56 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; + break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; break; default: return -1; @@ -618,3 +700,125 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_server_set_log_cb(struct tlsv1_server *conn, + void (*cb)(void *ctx, const char *msg), void *ctx) +{ + conn->log_cb = cb; + conn->log_cb_ctx = ctx; +} + + +#ifdef CONFIG_TESTING_OPTIONS +void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) +{ + conn->test_flags = flags; +} + + +static const u8 test_tls_prime15[1] = { + 15 +}; + +static const u8 test_tls_prime511b[64] = { + 0x50, 0xfb, 0xf1, 0xae, 0x01, 0xf1, 0xfe, 0xe6, + 0xe1, 0xae, 0xdc, 0x1e, 0xbe, 0xfb, 0x9e, 0x58, + 0x9a, 0xd7, 0x54, 0x9d, 0x6b, 0xb3, 0x78, 0xe2, + 0x39, 0x7f, 0x30, 0x01, 0x25, 0xa1, 0xf9, 0x7c, + 0x55, 0x0e, 0xa1, 0x15, 0xcc, 0x36, 0x34, 0xbb, + 0x6c, 0x8b, 0x64, 0x45, 0x15, 0x7f, 0xd3, 0xe7, + 0x31, 0xc8, 0x8e, 0x56, 0x8e, 0x95, 0xdc, 0xea, + 0x9e, 0xdf, 0xf7, 0x56, 0xdd, 0xb0, 0x34, 0xdb +}; + +static const u8 test_tls_prime767b[96] = { + 0x4c, 0xdc, 0xb8, 0x21, 0x20, 0x9d, 0xe8, 0xa3, + 0x53, 0xd9, 0x1c, 0x18, 0xc1, 0x3a, 0x58, 0x67, + 0xa7, 0x85, 0xf9, 0x28, 0x9b, 0xce, 0xc0, 0xd1, + 0x05, 0x84, 0x61, 0x97, 0xb2, 0x86, 0x1c, 0xd0, + 0xd1, 0x96, 0x23, 0x29, 0x8c, 0xc5, 0x30, 0x68, + 0x3e, 0xf9, 0x05, 0xba, 0x60, 0xeb, 0xdb, 0xee, + 0x2d, 0xdf, 0x84, 0x65, 0x49, 0x87, 0x90, 0x2a, + 0xc9, 0x8e, 0x34, 0x63, 0x6d, 0x9a, 0x2d, 0x32, + 0x1c, 0x46, 0xd5, 0x4e, 0x20, 0x20, 0x90, 0xac, + 0xd5, 0x48, 0x79, 0x99, 0x0c, 0xe6, 0xed, 0xbf, + 0x79, 0xc2, 0x47, 0x50, 0x95, 0x38, 0x38, 0xbc, + 0xde, 0xb0, 0xd2, 0xe8, 0x97, 0xcb, 0x22, 0xbb +}; + +static const u8 test_tls_prime58[128] = { + 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, + 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, + 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, + 0x03, 0xc1, 0xba, 0xc8, 0x25, 0xbe, 0x2d, 0xf3 +}; + +static const u8 test_tls_non_prime[] = { + /* + * This is not a prime and the value has the following factors: + * 13736783488716579923 * 16254860191773456563 * 18229434976173670763 * + * 11112313018289079419 * 10260802278580253339 * 12394009491575311499 * + * 12419059668711064739 * 14317973192687985827 * 10498605410533203179 * + * 16338688760390249003 * 11128963991123878883 * 12990532258280301419 * + * 3 + */ + 0x0C, 0x8C, 0x36, 0x9C, 0x6F, 0x71, 0x2E, 0xA7, + 0xAB, 0x32, 0xD3, 0x0F, 0x68, 0x3D, 0xB2, 0x6D, + 0x81, 0xDD, 0xC4, 0x84, 0x0D, 0x9C, 0x6E, 0x36, + 0x29, 0x70, 0xF3, 0x1E, 0x9A, 0x42, 0x0B, 0x67, + 0x82, 0x6B, 0xB1, 0xF2, 0xAF, 0x55, 0x28, 0xE7, + 0xDB, 0x67, 0x6C, 0xF7, 0x6B, 0xAC, 0xAC, 0xE5, + 0xF7, 0x9F, 0xD4, 0x63, 0x55, 0x70, 0x32, 0x7C, + 0x70, 0xFB, 0xAF, 0xB8, 0xEB, 0x37, 0xCF, 0x3F, + 0xFE, 0x94, 0x73, 0xF9, 0x7A, 0xC7, 0x12, 0x2E, + 0x9B, 0xB4, 0x7D, 0x08, 0x60, 0x83, 0x43, 0x52, + 0x83, 0x1E, 0xA5, 0xFC, 0xFA, 0x87, 0x12, 0xF4, + 0x64, 0xE2, 0xCE, 0x71, 0x17, 0x72, 0xB6, 0xAB +}; + +#endif /* CONFIG_TESTING_OPTIONS */ + + +void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p, + size_t *dh_p_len) +{ + *dh_p = conn->cred->dh_p; + *dh_p_len = conn->cred->dh_p_len; + +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_DHE_PRIME_511B) { + tlsv1_server_log(conn, "TESTING: Use short 511-bit prime with DHE"); + *dh_p = test_tls_prime511b; + *dh_p_len = sizeof(test_tls_prime511b); + } else if (conn->test_flags & TLS_DHE_PRIME_767B) { + tlsv1_server_log(conn, "TESTING: Use short 767-bit prime with DHE"); + *dh_p = test_tls_prime767b; + *dh_p_len = sizeof(test_tls_prime767b); + } else if (conn->test_flags & TLS_DHE_PRIME_15) { + tlsv1_server_log(conn, "TESTING: Use bogus 15 \"prime\" with DHE"); + *dh_p = test_tls_prime15; + *dh_p_len = sizeof(test_tls_prime15); + } else if (conn->test_flags & TLS_DHE_PRIME_58B) { + tlsv1_server_log(conn, "TESTING: Use short 58-bit prime in long container with DHE"); + *dh_p = test_tls_prime58; + *dh_p_len = sizeof(test_tls_prime58); + } else if (conn->test_flags & TLS_DHE_NON_PRIME) { + tlsv1_server_log(conn, "TESTING: Use claim non-prime as the DHE prime"); + *dh_p = test_tls_non_prime; + *dh_p_len = sizeof(test_tls_non_prime); + } +#endif /* CONFIG_TESTING_OPTIONS */ +} diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index a18c69e37c16..b2b28d1e1215 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -45,4 +45,9 @@ 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/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 1f61533a5aa0..96d79b3a8ba2 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -51,13 +51,24 @@ struct tlsv1_server { tlsv1_server_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; + void (*log_cb)(void *ctx, const char *msg); + void *log_cb_ctx; + int use_session_ticket; u8 *dh_secret; size_t dh_secret_len; + +#ifdef CONFIG_TESTING_OPTIONS + u32 test_flags; + int test_failure_reported; +#endif /* CONFIG_TESTING_OPTIONS */ }; +void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); int tlsv1_server_derive_keys(struct tlsv1_server *conn, const u8 *pre_master_secret, @@ -67,5 +78,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, u8 description, size_t *out_len); int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, const u8 *buf, size_t *len); +void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p, + size_t *dh_p_len); #endif /* TLSV1_SERVER_I_H */ diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 6f6539b1bfb5..0f237baff9db 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -1,6 +1,6 @@ /* * TLSv1 server - read handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,25 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, size_t *in_len); +static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) +{ +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE | + TLS_DHE_PRIME_511B | TLS_DHE_PRIME_767B | TLS_DHE_PRIME_15 | + TLS_DHE_PRIME_58B | TLS_DHE_NON_PRIME)) && + suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 && + suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA && + suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 && + suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA && + suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) + return 1; +#endif /* CONFIG_TESTING_OPTIONS */ + + return 0; +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -38,8 +57,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, u16 ext_type, ext_len; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -53,13 +72,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected ClientHello)", *pos); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientHello)", + *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); + tlsv1_server_log(conn, "Received ClientHello"); pos++; /* uint24 length */ len = WPA_GET_BE24(pos); @@ -78,13 +97,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, if (end - pos < 2) goto decode_error; conn->client_version = WPA_GET_BE16(pos); - wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", - conn->client_version >> 8, conn->client_version & 0xff); + tlsv1_server_log(conn, "Client version %d.%d", + conn->client_version >> 8, + conn->client_version & 0xff); if (conn->client_version < TLS_VERSION_1) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello %u.%u", - conn->client_version >> 8, - conn->client_version & 0xff); + tlsv1_server_log(conn, "Unexpected protocol version in ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; @@ -101,8 +120,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->rl.tls_version = TLS_VERSION_1_1; else conn->rl.tls_version = conn->client_version; - wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", - tls_version_str(conn->rl.tls_version)); + tlsv1_server_log(conn, "Using TLS v%s", + tls_version_str(conn->rl.tls_version)); /* Random random */ if (end - pos < TLS_RANDOM_LEN) @@ -137,6 +156,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, cipher_suite = 0; for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { + if (testing_cipher_suite_filter(conn, conn->cipher_suites[i])) + continue; c = pos; for (j = 0; j < num_suites; j++) { u16 tmp = WPA_GET_BE16(c); @@ -149,8 +170,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, } pos += num_suites * 2; if (!cipher_suite) { - wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " - "available"); + tlsv1_server_log(conn, "No supported cipher suite available"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; @@ -180,16 +200,15 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, compr_null_found = 1; } if (!compr_null_found) { - wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " - "compression"); + tlsv1_server_log(conn, "Client does not accept NULL compression"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (end - pos == 1) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " - "end of ClientHello: 0x%02x", *pos); + tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x", + *pos); goto decode_error; } @@ -198,12 +217,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len = WPA_GET_BE16(pos); pos += 2; - wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " - "extensions", ext_len); + tlsv1_server_log(conn, "%u bytes of ClientHello extensions", + ext_len); if (end - pos != ext_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " - "extension list length %u (expected %u)", - ext_len, (unsigned int) (end - pos)); + tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)", + ext_len, (unsigned int) (end - pos)); goto decode_error; } @@ -216,8 +234,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, while (pos < end) { if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_type field"); + tlsv1_server_log(conn, "Invalid extension_type field"); goto decode_error; } @@ -225,8 +242,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 2; if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_data length field"); + tlsv1_server_log(conn, "Invalid extension_data length field"); goto decode_error; } @@ -234,13 +250,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 2; if (end - pos < ext_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_data field"); + tlsv1_server_log(conn, "Invalid extension_data field"); goto decode_error; } - wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " - "type %u", ext_type); + tlsv1_server_log(conn, "ClientHello Extension type %u", + ext_type); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " "Extension data", pos, ext_len); @@ -260,14 +275,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, *in_len = end - in_data; - wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " - "ServerHello"); + tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello"); conn->state = SERVER_HELLO; return 0; decode_error: - wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); + tlsv1_server_log(conn, "Failed to decode ClientHello"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -284,8 +298,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, int reason; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -295,8 +309,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " - "(len=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short Certificate message (len=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -308,9 +322,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " - "length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Unexpected Certificate message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -318,8 +331,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { if (conn->verify_peer) { - wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " - "Certificate"); + tlsv1_server_log(conn, "Client did not include Certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -329,17 +341,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, in_len); } if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected Certificate/" - "ClientKeyExchange)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected Certificate/ClientKeyExchange)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, - "TLSv1: Received Certificate (certificate_list len %lu)", - (unsigned long) len); + tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)", + (unsigned long) len); /* * opaque ASN.1Cert<2^24-1>; @@ -352,8 +362,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, end = pos + len; if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " - "(left=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short Certificate (left=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -363,10 +373,9 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, pos += 3; if ((size_t) (end - pos) != list_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " - "length (len=%lu left=%lu)", - (unsigned long) list_len, - (unsigned long) (end - pos)); + tlsv1_server_log(conn, "Unexpected certificate_list length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -375,8 +384,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, idx = 0; while (pos < end) { if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "certificate_list"); + tlsv1_server_log(conn, "Failed to parse certificate_list"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); @@ -387,25 +395,23 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, pos += 3; if ((size_t) (end - pos) < cert_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " - "length (len=%lu left=%lu)", - (unsigned long) cert_len, - (unsigned long) (end - pos)); + tlsv1_server_log(conn, "Unexpected certificate length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", - (unsigned long) idx, (unsigned long) cert_len); + tlsv1_server_log(conn, "Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); if (idx == 0) { crypto_public_key_free(conn->client_rsa_key); if (tls_parse_cert(pos, cert_len, &conn->client_rsa_key)) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "the certificate"); + tlsv1_server_log(conn, "Failed to parse the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); @@ -415,8 +421,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, cert = x509_certificate_parse(pos, cert_len); if (cert == NULL) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "the certificate"); + tlsv1_server_log(conn, "Failed to parse the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); @@ -436,8 +441,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, &reason, 0) < 0) { int tls_reason; - wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " - "validation failed (reason=%d)", reason); + tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)", + reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -494,9 +499,8 @@ static int tls_process_client_key_exchange_rsa( encr_len = WPA_GET_BE16(pos); pos += 2; if (pos + encr_len > end) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " - "format: encr_len=%u left=%u", - encr_len, (unsigned int) (end - pos)); + tlsv1_server_log(conn, "Invalid ClientKeyExchange format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -539,15 +543,13 @@ static int tls_process_client_key_exchange_rsa( } if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " - "length %lu", (unsigned long) outlen); + tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu", + (unsigned long) outlen); use_random = 1; } if (!use_random && WPA_GET_BE16(out) != conn->client_version) { - wpa_printf(MSG_DEBUG, "TLSv1: Client version in " - "ClientKeyExchange does not match with version in " - "ClientHello"); + tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello"); use_random = 1; } @@ -582,7 +584,7 @@ static int tls_process_client_key_exchange_rsa( } -static int tls_process_client_key_exchange_dh_anon( +static int tls_process_client_key_exchange_dh( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { const u8 *dh_yc; @@ -590,6 +592,8 @@ static int tls_process_client_key_exchange_dh_anon( u8 *shared; size_t shared_len; int res; + const u8 *dh_p; + size_t dh_p_len; /* * struct { @@ -600,6 +604,7 @@ static int tls_process_client_key_exchange_dh_anon( * } ClientDiffieHellmanPublic; */ + tlsv1_server_log(conn, "ClientDiffieHellmanPublic received"); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", pos, end - pos); @@ -612,8 +617,7 @@ static int tls_process_client_key_exchange_dh_anon( } if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " - "length"); + tlsv1_server_log(conn, "Invalid client public value length"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -622,9 +626,9 @@ static int tls_process_client_key_exchange_dh_anon( dh_yc_len = WPA_GET_BE16(pos); dh_yc = pos + 2; - if (dh_yc + dh_yc_len > end) { - wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " - "(length %d)", dh_yc_len); + if (dh_yc_len > end - dh_yc) { + tlsv1_server_log(conn, "Client public value overflow (length %d)", + dh_yc_len); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -641,7 +645,9 @@ static int tls_process_client_key_exchange_dh_anon( return -1; } - shared_len = conn->cred->dh_p_len; + tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len); + + shared_len = dh_p_len; shared = os_malloc(shared_len); if (shared == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " @@ -653,8 +659,7 @@ static int tls_process_client_key_exchange_dh_anon( /* shared = Yc^secret mod p */ if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret, - conn->dh_secret_len, - conn->cred->dh_p, conn->cred->dh_p_len, + conn->dh_secret_len, dh_p, dh_p_len, shared, &shared_len)) { os_free(shared); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -695,8 +700,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, const struct tls_cipher_suite *suite; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -706,8 +711,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " - "(Left=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -719,9 +724,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " - "length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Mismatch in ClientKeyExchange length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -730,14 +734,14 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected ClientKeyExchange)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientKeyExchange)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); + tlsv1_server_log(conn, "Received ClientKeyExchange"); wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); @@ -747,11 +751,11 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, else keyx = suite->key_exchange; - if (keyx == TLS_KEY_X_DH_anon && - tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) + if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) && + tls_process_client_key_exchange_dh(conn, pos, end) < 0) return -1; - if (keyx != TLS_KEY_X_DH_anon && + if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA && tls_process_client_key_exchange_rsa(conn, pos, end) < 0) return -1; @@ -769,15 +773,13 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, const u8 *pos, *end; size_t left, len; u8 type; - size_t hlen, buflen; - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; - enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; - u16 slen; + size_t hlen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; + u8 alert; if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { if (conn->verify_peer) { - wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " - "CertificateVerify"); + tlsv1_server_log(conn, "Client did not include CertificateVerify"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -788,8 +790,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, } if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -799,8 +801,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " - "message (len=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -812,9 +814,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " - "message length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Unexpected CertificateVerify message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -823,14 +824,14 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected CertificateVerify)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected CertificateVerify)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); + tlsv1_server_log(conn, "Received CertificateVerify"); /* * struct { @@ -881,21 +882,17 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, } else { #endif /* CONFIG_TLSV12 */ - if (alg == SIGN_ALG_RSA) { - hlen = MD5_MAC_LEN; - if (conn->verify.md5_cert == NULL || - crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) - { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - conn->verify.md5_cert = NULL; - crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); - conn->verify.sha1_cert = NULL; - return -1; - } - hpos += MD5_MAC_LEN; - } else - crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; @@ -908,8 +905,7 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, } conn->verify.sha1_cert = NULL; - if (alg == SIGN_ALG_RSA) - hlen += MD5_MAC_LEN; + hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } @@ -917,90 +913,13 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); - if (end - pos < 2) { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECODE_ERROR); - return -1; - } - slen = WPA_GET_BE16(pos); - pos += 2; - if (end - pos < slen) { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECODE_ERROR); + if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key, + hash, hlen, pos, end - pos, &alert) < 0) { + tlsv1_server_log(conn, "Invalid Signature in CertificateVerify"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); - if (conn->client_rsa_key == NULL) { - wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " - "signature"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - return -1; - } - - buflen = end - pos; - buf = os_malloc(end - pos); - if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, - pos, end - pos, buf, &buflen) < 0) - { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - - wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", - buf, buflen); - -#ifdef CONFIG_TLSV12 - if (conn->rl.tls_version >= TLS_VERSION_1_2) { - /* - * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 - * - * DigestInfo ::= SEQUENCE { - * digestAlgorithm DigestAlgorithm, - * digest OCTET STRING - * } - * - * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} - * - * DER encoded DigestInfo for SHA256 per RFC 3447: - * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || - * H - */ - if (buflen >= 19 + 32 && - os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" - "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) - { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " - "SHA-256"); - os_memmove(buf, buf + 19, buflen - 19); - buflen -= 19; - } else { - wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " - "DigestInfo"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - } -#endif /* CONFIG_TLSV12 */ - - if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " - "CertificateVerify - did not match with calculated " - "hash"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - - os_free(buf); - *in_len = end - in_data; conn->state = CHANGE_CIPHER_SPEC; @@ -1017,8 +936,8 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, size_t left; if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected ChangeCipherSpec; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -1028,21 +947,21 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, left = *in_len; if (left < 1) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tlsv1_server_log(conn, "Too short ChangeCipherSpec"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (*pos != TLS_CHANGE_CIPHER_SPEC) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " - "received data 0x%x", *pos); + tlsv1_server_log(conn, "Expected ChangeCipherSpec; received data 0x%x", + *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + tlsv1_server_log(conn, "Received ChangeCipherSpec"); if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " "for record layer"); @@ -1067,9 +986,48 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_15) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after bogus DHE \"prime\" 15"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_58B) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after short 58-bit DHE prime in long container"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_511B) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-WARNING: Client Finished received after short 511-bit DHE prime (insecure)"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_767B) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after 767-bit DHE prime (relatively insecure)"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_NON_PRIME) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after non-prime claimed as DHE prime"); + conn->test_failure_reported = 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Finished; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -1079,9 +1037,8 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " - "Finished", - (unsigned long) left); + tlsv1_server_log(conn, "Too short record (left=%lu) forFinished", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -1101,18 +1058,16 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " - "(len=%lu > left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Too short buffer for Finished (len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (len != TLS_VERIFY_DATA_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " - "in Finished: %lu (expected %d)", - (unsigned long) len, TLS_VERIFY_DATA_LEN); + tlsv1_server_log(conn, "Unexpected verify_data length in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -1174,19 +1129,18 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", verify_data, TLS_VERIFY_DATA_LEN); - if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { - wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + tlsv1_server_log(conn, "Mismatch in verify_data"); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + tlsv1_server_log(conn, "Received Finished"); *in_len = end - in_data; if (conn->use_session_ticket) { /* Abbreviated handshake using session ticket; RFC 4507 */ - wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " - "successfully"); + tlsv1_server_log(conn, "Abbreviated handshake completed successfully"); conn->state = ESTABLISHED; } else { /* Full handshake */ @@ -1202,13 +1156,12 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, { if (ct == TLS_CONTENT_TYPE_ALERT) { if (*len < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", - buf[0], buf[1]); + tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]); *len = 2; conn->state = FAILED; return -1; @@ -1240,9 +1193,8 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, return -1; break; default: - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " - "while processing received message", - conn->state); + tlsv1_server_log(conn, "Unexpected state %d while processing received message", + conn->state); return -1; } diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index 6d8e55ed49a0..15e6692178ff 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -1,6 +1,6 @@ /* * TLSv1 server - write handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -48,7 +48,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); + tlsv1_server_log(conn, "Send ServerHello"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -104,8 +104,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, conn->client_random, conn->server_random, conn->master_secret); if (res < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " - "indicated failure"); + tlsv1_server_log(conn, "SessionTicket callback indicated failure"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; @@ -170,7 +169,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + tlsv1_server_log(conn, "Send Certificate"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -245,10 +244,12 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, { tls_key_exchange keyx; const struct tls_cipher_suite *suite; - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *server_params; size_t rlen; u8 *dh_ys; size_t dh_ys_len; + const u8 *dh_p; + size_t dh_p_len; suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) @@ -261,8 +262,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return 0; } - if (keyx != TLS_KEY_X_DH_anon) { - /* TODO? */ + if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) { wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " "supported with key exchange type %d", keyx); return -1; @@ -275,8 +275,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } + tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len); + os_free(conn->dh_secret); - conn->dh_secret_len = conn->cred->dh_p_len; + conn->dh_secret_len = dh_p_len; conn->dh_secret = os_malloc(conn->dh_secret_len); if (conn->dh_secret == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " @@ -295,8 +297,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } - if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > - 0) + if (os_memcmp(conn->dh_secret, dh_p, conn->dh_secret_len) > 0) conn->dh_secret[0] = 0; /* make sure secret < p */ pos = conn->dh_secret; @@ -311,7 +312,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, conn->dh_secret, conn->dh_secret_len); /* Ys = g^secret mod p */ - dh_ys_len = conn->cred->dh_p_len; + dh_ys_len = dh_p_len; dh_ys = os_malloc(dh_ys_len); if (dh_ys == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " @@ -322,8 +323,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, } if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, conn->dh_secret, conn->dh_secret_len, - conn->cred->dh_p, conn->cred->dh_p_len, - dh_ys, &dh_ys_len)) { + dh_p, dh_p_len, dh_ys, &dh_ys_len)) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); os_free(dh_ys); @@ -354,7 +354,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); + tlsv1_server_log(conn, "Send ServerKeyExchange"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -369,8 +369,9 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += 3; /* body - ServerDHParams */ + server_params = pos; /* dh_p */ - if (pos + 2 + conn->cred->dh_p_len > end) { + if (pos + 2 + dh_p_len > end) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_p"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -378,10 +379,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, os_free(dh_ys); return -1; } - WPA_PUT_BE16(pos, conn->cred->dh_p_len); + WPA_PUT_BE16(pos, dh_p_len); pos += 2; - os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); - pos += conn->cred->dh_p_len; + os_memcpy(pos, dh_p, dh_p_len); + pos += dh_p_len; /* dh_g */ if (pos + 2 + conn->cred->dh_g_len > end) { @@ -412,6 +413,138 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += dh_ys_len; os_free(dh_ys); + /* + * select (SignatureAlgorithm) + * { case anonymous: struct { }; + * case rsa: + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * case dsa: + * digitally-signed struct { + * opaque sha_hash[20]; + * }; + * } Signature; + * + * md5_hash + * MD5(ClientHello.random + ServerHello.random + ServerParams); + * + * sha_hash + * SHA(ClientHello.random + ServerHello.random + ServerParams); + */ + + if (keyx == TLS_KEY_X_DHE_RSA) { + u8 hash[100]; + u8 *signed_start; + size_t clen; + int hlen; + + if (conn->rl.tls_version >= TLS_VERSION_1_2) { +#ifdef CONFIG_TLSV12 + hlen = tlsv12_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + pos - server_params, hash + 19); + + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used + * signature and hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (hlen < 0 || pos + 2 > end) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 + * 04 20 || H + */ + hlen += 19; + os_memcpy(hash, + "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + +#else /* CONFIG_TLSV12 */ + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; +#endif /* CONFIG_TLSV12 */ + } else { + hlen = tls_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + pos - server_params, hash); + } + + if (hlen < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash", + hash, hlen); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) { + tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash"); + hash[hlen - 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* + * RFC 2246, 4.7: + * In digital signing, one-way hash functions are used as input + * for a signing algorithm. A digitally-signed element is + * encoded as an opaque vector <0..2^16-1>, where the length is + * specified by the signing algorithm and key. + * + * In RSA signing, a 36-byte structure of two hashes (one SHA + * and one MD5) is signed (encrypted with the private key). It + * is encoded with PKCS #1 block type 0 or type 1 as described + * in [PKCS1]. + */ + signed_start = pos; /* length to be filled */ + pos += 2; + clen = end - pos; + if (conn->cred == NULL || + crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, + pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(signed_start, clen); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) { + tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature"); + pos[clen - 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + pos += clen; + } + WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, @@ -445,7 +578,7 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); + tlsv1_server_log(conn, "Send CertificateRequest"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -505,7 +638,7 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn, size_t rlen; u8 payload[4]; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); + tlsv1_server_log(conn, "Send ServerHelloDone"); /* opaque fragment[TLSPlaintext.length] */ @@ -541,7 +674,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, size_t rlen; u8 payload[1]; - wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + tlsv1_server_log(conn, "Send ChangeCipherSpec"); payload[0] = TLS_CHANGE_CIPHER_SPEC; @@ -578,7 +711,7 @@ static int tls_write_server_finished(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + tlsv1_server_log(conn, "Send Finished"); /* Encrypted Handshake Message: Finished */ @@ -635,6 +768,12 @@ static int tls_write_server_finished(struct tlsv1_server *conn, } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_VERIFY_DATA) { + tlsv1_server_log(conn, "TESTING: Break verify_data (server)"); + verify_data[1 + 3 + 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ /* Handshake */ pos = hs_start = verify_data; @@ -736,7 +875,7 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, *out_len = pos - msg; - wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); + tlsv1_server_log(conn, "Handshake completed successfully"); conn->state = ESTABLISHED; return msg; @@ -755,8 +894,8 @@ u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) /* Abbreviated handshake was already completed. */ return NULL; } - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " - "generating reply", conn->state); + tlsv1_server_log(conn, "Unexpected state %d while generating reply", + conn->state); return NULL; } } @@ -767,7 +906,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, { u8 *alert, *pos, *length; - wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description); *out_len = 0; alert = os_malloc(10); diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index 87c51784ea71..742af328cf8c 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -443,17 +443,16 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, return -1; } - val = os_malloc(hdr.length + 1); + val = dup_binstr(hdr.payload, hdr.length); if (val == NULL) { x509_free_name(name); return -1; } - os_memcpy(val, hdr.payload, hdr.length); - val[hdr.length] = '\0'; if (os_strlen(val) != hdr.length) { wpa_printf(MSG_INFO, "X509: Reject certificate with " "embedded NUL byte in a string (%s[NUL])", val); + os_free(val); x509_free_name(name); return -1; } @@ -513,7 +512,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) ret = os_snprintf(pos, end - pos, "%s=%s, ", x509_name_attr_str(name->attr[i].type), name->attr[i].value); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) goto done; pos += ret; } @@ -528,7 +527,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) if (name->email) { ret = os_snprintf(pos, end - pos, "/emailAddress=%s", name->email); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) goto done; pos += ret; } @@ -1349,7 +1348,8 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ - if (hdr.payload + hdr.length == end) + pos = hdr.payload + hdr.length; + if (pos == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || @@ -1367,7 +1367,8 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ - if (hdr.payload + hdr.length == end) + pos = hdr.payload + hdr.length; + if (pos == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || @@ -1775,13 +1776,22 @@ skip_digest_oid: } if (hdr.length != hash_len || - os_memcmp(hdr.payload, hash, hdr.length) != 0) { + os_memcmp_const(hdr.payload, hash, hdr.length) != 0) { wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " "with calculated tbsCertificate hash"); os_free(data); return -1; } + if (hdr.payload + hdr.length < data + data_len) { + wpa_hexdump(MSG_INFO, + "X509: Extra data after certificate signature hash", + hdr.payload + hdr.length, + data + data_len - hdr.payload - hdr.length); + os_free(data); + return -1; + } + os_free(data); wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " diff --git a/src/utils/Makefile b/src/utils/Makefile index 0f1f19109995..8aad813cfc85 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -1,7 +1,7 @@ all: libutils.a clean: - rm -f *~ *.o *.d libutils.a + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a install: @echo Nothing to be made. @@ -11,9 +11,11 @@ include ../lib.rules #CFLAGS += -DWPA_TRACE CFLAGS += -DCONFIG_IPV6 +CFLAGS += -DCONFIG_DEBUG_FILE LIB_OBJS= \ base64.o \ + bitfield.o \ common.o \ ip_addr.o \ radiotap.o \ diff --git a/src/utils/base64.c b/src/utils/base64.c index af1307fc4e65..d44f290e5684 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -48,9 +48,11 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, pos = out; line_len = 0; while (end - in >= 3) { - *pos++ = base64_table[in[0] >> 2]; - *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; - *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[(in[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; @@ -61,14 +63,14 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, } if (end - in) { - *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[(in[0] >> 2) & 0x3f]; if (end - in == 1) { - *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f]; *pos++ = '='; } else { - *pos++ = base64_table[((in[0] & 0x03) << 4) | - (in[1] >> 4)]; - *pos++ = base64_table[(in[1] & 0x0f) << 2]; + *pos++ = base64_table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f]; } *pos++ = '='; line_len += 4; diff --git a/src/utils/bitfield.c b/src/utils/bitfield.c new file mode 100644 index 000000000000..8dcec3907e9e --- /dev/null +++ b/src/utils/bitfield.c @@ -0,0 +1,89 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "bitfield.h" + + +struct bitfield { + u8 *bits; + size_t max_bits; +}; + + +struct bitfield * bitfield_alloc(size_t max_bits) +{ + struct bitfield *bf; + + bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8); + if (bf == NULL) + return NULL; + bf->bits = (u8 *) (bf + 1); + bf->max_bits = max_bits; + return bf; +} + + +void bitfield_free(struct bitfield *bf) +{ + os_free(bf); +} + + +void bitfield_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] |= BIT(bit % 8); +} + + +void bitfield_clear(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] &= ~BIT(bit % 8); +} + + +int bitfield_is_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return 0; + return !!(bf->bits[bit / 8] & BIT(bit % 8)); +} + + +static int first_zero(u8 val) +{ + int i; + for (i = 0; i < 8; i++) { + if (!(val & 0x01)) + return i; + val >>= 1; + } + return -1; +} + + +int bitfield_get_first_zero(struct bitfield *bf) +{ + size_t i; + for (i = 0; i < (bf->max_bits + 7) / 8; i++) { + if (bf->bits[i] != 0xff) + break; + } + if (i == (bf->max_bits + 7) / 8) + return -1; + i = i * 8 + first_zero(bf->bits[i]); + if (i >= bf->max_bits) + return -1; + return i; +} diff --git a/src/utils/bitfield.h b/src/utils/bitfield.h new file mode 100644 index 000000000000..7050a208c8f6 --- /dev/null +++ b/src/utils/bitfield.h @@ -0,0 +1,21 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BITFIELD_H +#define BITFIELD_H + +struct bitfield; + +struct bitfield * bitfield_alloc(size_t max_bits); +void bitfield_free(struct bitfield *bf); +void bitfield_set(struct bitfield *bf, size_t bit); +void bitfield_clear(struct bitfield *bf, size_t bit); +int bitfield_is_set(struct bitfield *bf, size_t bit); +int bitfield_get_first_zero(struct bitfield *bf); + +#endif /* BITFIELD_H */ diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c new file mode 100644 index 000000000000..9ce1a5cbeae1 --- /dev/null +++ b/src/utils/browser-android.c @@ -0,0 +1,128 @@ +/* + * Hotspot 2.0 client - Web browser using Android browser + * Copyright (c) 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 "utils/eloop.h" +#include "wps/http_server.h" +#include "browser.h" + + +struct browser_data { + int success; +}; + + +static void browser_timeout(void *eloop_data, void *user_ctx) +{ + wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to " + "complete"); + eloop_terminate(); +} + + +static void http_req(void *ctx, struct http_request *req) +{ + struct browser_data *data = ctx; + struct wpabuf *resp; + const char *url; + int done = 0; + + url = http_request_get_uri(req); + wpa_printf(MSG_INFO, "Browser response received: %s", url); + + if (os_strcmp(url, "/") == 0) { + data->success = 1; + done = 1; + } else if (os_strncmp(url, "/osu/", 5) == 0) { + data->success = atoi(url + 5); + done = 1; + } + + resp = wpabuf_alloc(1); + if (resp == NULL) { + http_request_deinit(req); + if (done) + eloop_terminate(); + return; + } + + if (done) { + eloop_cancel_timeout(browser_timeout, NULL, NULL); + eloop_register_timeout(0, 500000, browser_timeout, &data, NULL); + } + + http_request_send_and_deinit(req, resp); +} + + +int hs20_web_browser(const char *url) +{ + struct http_server *http; + struct in_addr addr; + struct browser_data data; + pid_t pid; + + wpa_printf(MSG_INFO, "Launching Android browser to %s", url); + + os_memset(&data, 0, sizeof(data)); + + if (eloop_init() < 0) { + wpa_printf(MSG_ERROR, "eloop_init failed"); + return -1; + } + addr.s_addr = htonl((127 << 24) | 1); + http = http_server_init(&addr, 12345, http_req, &data); + if (http == NULL) { + wpa_printf(MSG_ERROR, "http_server_init failed"); + eloop_destroy(); + return -1; + } + + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); + http_server_deinit(http); + eloop_destroy(); + return -1; + } + + if (pid == 0) { + /* run the external command in the child process */ + char *argv[9]; + + argv[0] = "browser-android"; + argv[1] = "start"; + argv[2] = "-a"; + argv[3] = "android.intent.action.VIEW"; + argv[4] = "-d"; + argv[5] = (void *) url; + argv[6] = "-n"; + argv[7] = "com.android.browser/.BrowserActivity"; + argv[8] = NULL; + + execv("/system/bin/am", argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + exit(0); + return -1; + } + + eloop_register_timeout(30, 0, browser_timeout, &data, NULL); + eloop_run(); + eloop_cancel_timeout(browser_timeout, &data, NULL); + http_server_deinit(http); + eloop_destroy(); + + wpa_printf(MSG_INFO, "Closing Android browser"); + if (system("/system/bin/input keyevent KEYCODE_HOME") != 0) { + wpa_printf(MSG_INFO, "Failed to inject keyevent"); + } + + return data.success; +} diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c new file mode 100644 index 000000000000..aed39706c2e1 --- /dev/null +++ b/src/utils/browser-system.c @@ -0,0 +1,119 @@ +/* + * Hotspot 2.0 client - Web browser using system browser + * Copyright (c) 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 "utils/eloop.h" +#include "wps/http_server.h" +#include "browser.h" + + +struct browser_data { + int success; +}; + + +static void browser_timeout(void *eloop_data, void *user_ctx) +{ + wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to " + "complete"); + eloop_terminate(); +} + + +static void http_req(void *ctx, struct http_request *req) +{ + struct browser_data *data = ctx; + struct wpabuf *resp; + const char *url; + int done = 0; + + url = http_request_get_uri(req); + wpa_printf(MSG_INFO, "Browser response received: %s", url); + + if (os_strcmp(url, "/") == 0) { + data->success = 1; + done = 1; + } else if (os_strncmp(url, "/osu/", 5) == 0) { + data->success = atoi(url + 5); + done = 1; + } + + resp = wpabuf_alloc(1); + if (resp == NULL) { + http_request_deinit(req); + if (done) + eloop_terminate(); + return; + } + + if (done) { + eloop_cancel_timeout(browser_timeout, NULL, NULL); + eloop_register_timeout(0, 500000, browser_timeout, &data, NULL); + } + + http_request_send_and_deinit(req, resp); +} + + +int hs20_web_browser(const char *url) +{ + struct http_server *http; + struct in_addr addr; + struct browser_data data; + pid_t pid; + + wpa_printf(MSG_INFO, "Launching system browser to %s", url); + + os_memset(&data, 0, sizeof(data)); + + if (eloop_init() < 0) { + wpa_printf(MSG_ERROR, "eloop_init failed"); + return -1; + } + addr.s_addr = htonl((127 << 24) | 1); + http = http_server_init(&addr, 12345, http_req, &data); + if (http == NULL) { + wpa_printf(MSG_ERROR, "http_server_init failed"); + eloop_destroy(); + return -1; + } + + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); + http_server_deinit(http); + eloop_destroy(); + return -1; + } + + if (pid == 0) { + /* run the external command in the child process */ + char *argv[3]; + + argv[0] = "browser-system"; + argv[1] = (void *) url; + argv[2] = NULL; + + execv("/usr/bin/x-www-browser", argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + exit(0); + return -1; + } + + eloop_register_timeout(120, 0, browser_timeout, &data, NULL); + eloop_run(); + eloop_cancel_timeout(browser_timeout, &data, NULL); + http_server_deinit(http); + eloop_destroy(); + + /* TODO: Close browser */ + + return data.success; +} diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c new file mode 100644 index 000000000000..5fc40fac610e --- /dev/null +++ b/src/utils/browser-wpadebug.c @@ -0,0 +1,136 @@ +/* + * Hotspot 2.0 client - Web browser using wpadebug on Android + * Copyright (c) 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 "utils/eloop.h" +#include "wps/http_server.h" +#include "browser.h" + + +struct browser_data { + int success; +}; + + +static void browser_timeout(void *eloop_data, void *user_ctx) +{ + wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to " + "complete"); + eloop_terminate(); +} + + +static void http_req(void *ctx, struct http_request *req) +{ + struct browser_data *data = ctx; + struct wpabuf *resp; + const char *url; + int done = 0; + + url = http_request_get_uri(req); + wpa_printf(MSG_INFO, "Browser response received: %s", url); + + if (os_strcmp(url, "/") == 0) { + data->success = 1; + done = 1; + } else if (os_strncmp(url, "/osu/", 5) == 0) { + data->success = atoi(url + 5); + done = 1; + } + + resp = wpabuf_alloc(100); + if (resp == NULL) { + http_request_deinit(req); + if (done) + eloop_terminate(); + return; + } + wpabuf_put_str(resp, "User input completed"); + + if (done) { + eloop_cancel_timeout(browser_timeout, NULL, NULL); + eloop_register_timeout(0, 500000, browser_timeout, &data, NULL); + } + + http_request_send_and_deinit(req, resp); +} + + +int hs20_web_browser(const char *url) +{ + struct http_server *http; + struct in_addr addr; + struct browser_data data; + pid_t pid; + + wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url); + + os_memset(&data, 0, sizeof(data)); + + if (eloop_init() < 0) { + wpa_printf(MSG_ERROR, "eloop_init failed"); + return -1; + } + addr.s_addr = htonl((127 << 24) | 1); + http = http_server_init(&addr, 12345, http_req, &data); + if (http == NULL) { + wpa_printf(MSG_ERROR, "http_server_init failed"); + eloop_destroy(); + return -1; + } + + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); + http_server_deinit(http); + eloop_destroy(); + return -1; + } + + if (pid == 0) { + /* run the external command in the child process */ + char *argv[12]; + + argv[0] = "browser-wpadebug"; + argv[1] = "start"; + argv[2] = "-a"; + argv[3] = "android.action.MAIN"; + argv[4] = "-c"; + argv[5] = "android.intent.category.LAUNCHER"; + argv[6] = "-n"; + argv[7] = "w1.fi.wpadebug/.WpaWebViewActivity"; + argv[8] = "-e"; + argv[9] = "w1.fi.wpadebug.URL"; + argv[10] = (void *) url; + argv[11] = NULL; + + execv("/system/bin/am", argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + exit(0); + return -1; + } + + eloop_register_timeout(300, 0, browser_timeout, &data, NULL); + eloop_run(); + eloop_cancel_timeout(browser_timeout, &data, NULL); + http_server_deinit(http); + eloop_destroy(); + + wpa_printf(MSG_INFO, "Closing Android browser"); + if (os_exec("/system/bin/am", + "start -a android.action.MAIN " + "-c android.intent.category.LAUNCHER " + "-n w1.fi.wpadebug/.WpaWebViewActivity " + "-e w1.fi.wpadebug.URL FINISH", 1) != 0) { + wpa_printf(MSG_INFO, "Failed to close wpadebug browser"); + } + + return data.success; +} diff --git a/src/utils/browser.c b/src/utils/browser.c new file mode 100644 index 000000000000..9cf6152d6cbd --- /dev/null +++ b/src/utils/browser.c @@ -0,0 +1,219 @@ +/* + * Hotspot 2.0 client - Web browser using WebKit + * Copyright (c) 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 + +#include "common.h" +#include "browser.h" + + +struct browser_context { + GtkWidget *win; + int success; + int progress; + char *hover_link; + char *title; +}; + +static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx) +{ + wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__); + gtk_main_quit(); +} + + +static void browser_update_title(struct browser_context *ctx) +{ + char buf[100]; + + if (ctx->hover_link) { + gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link); + return; + } + + if (ctx->progress == 100) { + gtk_window_set_title(GTK_WINDOW(ctx->win), + ctx->title ? ctx->title : + "Hotspot 2.0 client"); + return; + } + + snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress, + ctx->title ? ctx->title : "Hotspot 2.0 client"); + gtk_window_set_title(GTK_WINDOW(ctx->win), buf); +} + + +static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec, + struct browser_context *ctx) +{ + ctx->progress = 100 * webkit_web_view_get_progress(view); + wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__, + ctx->progress); + browser_update_title(ctx); +} + + +static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec, + struct browser_context *ctx) +{ + int status = webkit_web_view_get_load_status(view); + wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s", + __func__, status, webkit_web_view_get_uri(view)); +} + + +static void view_cb_resource_request_starting(WebKitWebView *view, + WebKitWebFrame *frame, + WebKitWebResource *res, + WebKitNetworkRequest *req, + WebKitNetworkResponse *resp, + struct browser_context *ctx) +{ + const gchar *uri = webkit_network_request_get_uri(req); + wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri); + if (g_str_has_suffix(uri, "/favicon.ico")) + webkit_network_request_set_uri(req, "about:blank"); + if (g_str_has_prefix(uri, "osu://")) { + ctx->success = atoi(uri + 6); + gtk_main_quit(); + } + if (g_str_has_prefix(uri, "http://localhost:12345")) { + /* + * This is used as a special trigger to indicate that the + * user exchange has been completed. + */ + ctx->success = 1; + gtk_main_quit(); + } +} + + +static gboolean view_cb_mime_type_policy_decision( + WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req, + gchar *mime, WebKitWebPolicyDecision *policy, + struct browser_context *ctx) +{ + wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime); + + if (!webkit_web_view_can_show_mime_type(view, mime)) { + webkit_web_policy_decision_download(policy); + return TRUE; + } + + return FALSE; +} + + +static gboolean view_cb_download_requested(WebKitWebView *view, + WebKitDownload *dl, + struct browser_context *ctx) +{ + const gchar *uri; + uri = webkit_download_get_uri(dl); + wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri); + return FALSE; +} + + +static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title, + gchar *uri, struct browser_context *ctx) +{ + wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title, + uri); + os_free(ctx->hover_link); + if (uri) + ctx->hover_link = os_strdup(uri); + else + ctx->hover_link = NULL; + + browser_update_title(ctx); +} + + +static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame, + const char *title, + struct browser_context *ctx) +{ + wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title); + os_free(ctx->title); + ctx->title = os_strdup(title); + browser_update_title(ctx); +} + + +int hs20_web_browser(const char *url) +{ + GtkWidget *scroll; + SoupSession *s; + WebKitWebView *view; + WebKitWebSettings *settings; + struct browser_context ctx; + + memset(&ctx, 0, sizeof(ctx)); + if (!gtk_init_check(NULL, NULL)) + return -1; + + s = webkit_get_default_session(); + g_object_set(G_OBJECT(s), "ssl-ca-file", + "/etc/ssl/certs/ca-certificates.crt", NULL); + g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL); + + ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client", + "Hotspot 2.0 client"); + gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600); + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + + g_signal_connect(G_OBJECT(ctx.win), "destroy", + G_CALLBACK(win_cb_destroy), &ctx); + + view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + g_signal_connect(G_OBJECT(view), "notify::progress", + G_CALLBACK(view_cb_notify_progress), &ctx); + g_signal_connect(G_OBJECT(view), "notify::load-status", + G_CALLBACK(view_cb_notify_load_status), &ctx); + g_signal_connect(G_OBJECT(view), "resource-request-starting", + G_CALLBACK(view_cb_resource_request_starting), &ctx); + g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested", + G_CALLBACK(view_cb_mime_type_policy_decision), &ctx); + g_signal_connect(G_OBJECT(view), "download-requested", + G_CALLBACK(view_cb_download_requested), &ctx); + g_signal_connect(G_OBJECT(view), "hovering-over-link", + G_CALLBACK(view_cb_hovering_over_link), &ctx); + g_signal_connect(G_OBJECT(view), "title-changed", + G_CALLBACK(view_cb_title_changed), &ctx); + + gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); + gtk_widget_show_all(ctx.win); + + settings = webkit_web_view_get_settings(view); + g_object_set(G_OBJECT(settings), "user-agent", + "Mozilla/5.0 (X11; U; Unix; en-US) " + "AppleWebKit/537.15 (KHTML, like Gecko) " + "hs20-client/1.0", NULL); + g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL); + + webkit_web_view_load_uri(view, url); + + gtk_main(); + gtk_widget_destroy(ctx.win); + while (gtk_events_pending()) + gtk_main_iteration(); + + free(ctx.hover_link); + free(ctx.title); + return ctx.success; +} diff --git a/src/utils/browser.h b/src/utils/browser.h new file mode 100644 index 000000000000..aaa0eed269f7 --- /dev/null +++ b/src/utils/browser.h @@ -0,0 +1,21 @@ +/* + * Hotspot 2.0 client - Web browser + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BROWSER_H +#define BROWSER_H + +#ifdef CONFIG_NO_BROWSER +static inline int hs20_web_browser(const char *url) +{ + return -1; +} +#else /* CONFIG_NO_BROWSER */ +int hs20_web_browser(const char *url); +#endif /* CONFIG_NO_BROWSER */ + +#endif /* BROWSER_H */ diff --git a/src/utils/build_config.h b/src/utils/build_config.h index f947388552db..c6f4e4379ae6 100644 --- a/src/utils/build_config.h +++ b/src/utils/build_config.h @@ -47,31 +47,4 @@ #endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ -#ifdef CONFIG_XCODE_DEFAULTS -#define CONFIG_DRIVER_OSX -#define CONFIG_BACKEND_FILE -#define IEEE8021X_EAPOL -#define PKCS12_FUNCS -#define CONFIG_CTRL_IFACE -#define CONFIG_CTRL_IFACE_UNIX -#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 CONFIG_WPS -#define EAP_WSC - -#ifdef USE_INTERNAL_CRYPTO -#define CONFIG_TLS_INTERNAL_CLIENT -#define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_CRYPTO_INTERNAL -#endif /* USE_INTERNAL_CRYPTO */ -#endif /* CONFIG_XCODE_DEFAULTS */ - #endif /* BUILD_CONFIG_H */ diff --git a/src/utils/common.c b/src/utils/common.c index e63698491444..5fd795f3f303 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -36,6 +36,25 @@ int hex2byte(const char *hex) } +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") @@ -44,25 +63,46 @@ int hex2byte(const char *hex) */ int hwaddr_aton(const char *txt, u8 *addr) { - int i; + return hwaddr_parse(txt, addr) ? 0 : -1; +} - 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; - if (i < 5 && *txt++ != ':') +/** + * 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") @@ -144,6 +184,30 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len) } +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 @@ -183,6 +247,35 @@ void wpa_get_ntp_timestamp(u8 *buf) 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; +} static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) @@ -195,7 +288,7 @@ static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, for (i = 0; i < len; i++) { ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", data[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return pos - buf; } @@ -350,7 +443,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) size_t i; for (i = 0; i < len; i++) { - if (txt + 4 > end) + if (txt + 4 >= end) break; switch (data[i]) { @@ -362,7 +455,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) *txt++ = '\\'; *txt++ = '\\'; break; - case '\e': + case '\033': *txt++ = '\\'; *txt++ = 'e'; break; @@ -400,7 +493,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) int val; while (*pos) { - if (len == maxlen) + if (len + 1 >= maxlen) break; switch (*pos) { case '\\': @@ -427,7 +520,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) pos++; break; case 'e': - buf[len++] = '\e'; + buf[len++] = '\033'; pos++; break; case 'x': @@ -468,6 +561,8 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str) break; } } + if (maxlen > len) + buf[len] = '\0'; return len; } @@ -517,11 +612,9 @@ char * wpa_config_parse_string(const char *value, size_t *len) if (pos == NULL || pos[1] != '\0') return NULL; *len = pos - value; - str = os_malloc(*len + 1); + str = dup_binstr(value, *len); if (str == NULL) return NULL; - os_memcpy(str, value, *len); - str[*len] = '\0'; return str; } else if (*value == 'P' && value[1] == '"') { const char *pos; @@ -532,11 +625,9 @@ char * wpa_config_parse_string(const char *value, size_t *len) if (pos == NULL || pos[1] != '\0') return NULL; tlen = pos - value; - tstr = os_malloc(tlen + 1); + tstr = dup_binstr(value, tlen); if (tstr == NULL) return NULL; - os_memcpy(tstr, value, tlen); - tstr[tlen] = '\0'; str = os_malloc(tlen + 1); if (str == NULL) { @@ -610,3 +701,364 @@ size_t merge_byte_arrays(u8 *res, size_t res_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; +} + + +/** + * 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 *end, *pos = str; + + if (*context) + pos = *context; + + while (*pos && os_strchr(delim, *pos)) + pos++; + if (!*pos) + return NULL; + + end = pos + 1; + while (*end && !os_strchr(delim, *end)) + end++; + + if (*end) + *end++ = '\0'; + + *context = end; + return pos; +} + + +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; +} diff --git a/src/utils/common.h b/src/utils/common.h index 5fc916c0ab93..576e8e7e2d4e 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -164,6 +164,7 @@ static inline unsigned int wpa_swap_32(unsigned int v) #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) @@ -205,6 +206,7 @@ static inline unsigned int wpa_swap_32(unsigned int v) #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) @@ -224,74 +226,113 @@ static inline unsigned int wpa_swap_32(unsigned int v) /* Macros for handling unaligned memory accesses */ -#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) -#define WPA_PUT_BE16(a, val) \ - do { \ - (a)[0] = ((u16) (val)) >> 8; \ - (a)[1] = ((u16) (val)) & 0xff; \ - } while (0) - -#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) -#define WPA_PUT_LE16(a, val) \ - do { \ - (a)[1] = ((u16) (val)) >> 8; \ - (a)[0] = ((u16) (val)) & 0xff; \ - } while (0) - -#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ - ((u32) (a)[2])) -#define WPA_PUT_BE24(a, val) \ - do { \ - (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ - (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ - (a)[2] = (u8) (((u32) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ - (((u32) (a)[2]) << 8) | ((u32) (a)[3])) -#define WPA_PUT_BE32(a, val) \ - do { \ - (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ - (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ - (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ - (a)[3] = (u8) (((u32) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ - (((u32) (a)[1]) << 8) | ((u32) (a)[0])) -#define WPA_PUT_LE32(a, val) \ - do { \ - (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ - (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ - (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ - (a)[0] = (u8) (((u32) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_BE64(a) ((((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])) -#define WPA_PUT_BE64(a, val) \ - do { \ - (a)[0] = (u8) (((u64) (val)) >> 56); \ - (a)[1] = (u8) (((u64) (val)) >> 48); \ - (a)[2] = (u8) (((u64) (val)) >> 40); \ - (a)[3] = (u8) (((u64) (val)) >> 32); \ - (a)[4] = (u8) (((u64) (val)) >> 24); \ - (a)[5] = (u8) (((u64) (val)) >> 16); \ - (a)[6] = (u8) (((u64) (val)) >> 8); \ - (a)[7] = (u8) (((u64) (val)) & 0xff); \ - } while (0) - -#define WPA_GET_LE64(a) ((((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 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 (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 (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 @@ -422,17 +463,29 @@ typedef u64 __bitwise le64; #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(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); @@ -451,6 +504,7 @@ 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) { @@ -467,6 +521,39 @@ static inline int is_broadcast_ether_addr(const u8 *a) #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); + +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); + + /* * 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 diff --git a/src/utils/edit.c b/src/utils/edit.c index b01e08dfc2a9..d340bfadd1f1 100644 --- a/src/utils/edit.c +++ b/src/utils/edit.c @@ -14,7 +14,7 @@ #include "list.h" #include "edit.h" -#define CMD_BUF_LEN 256 +#define CMD_BUF_LEN 4096 static char cmdbuf[CMD_BUF_LEN]; static int cmdbuf_pos = 0; static int cmdbuf_len = 0; @@ -345,7 +345,7 @@ static void insert_char(int c) static void process_cmd(void) { - + currbuf_valid = 0; if (cmdbuf_len == 0) { printf("\n%s> ", ps2 ? ps2 : ""); fflush(stdout); diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c index add26fa33737..c2a5bcaa1785 100644 --- a/src/utils/edit_readline.c +++ b/src/utils/edit_readline.c @@ -167,9 +167,9 @@ void edit_deinit(const char *history_file, if (filter_cb && filter_cb(edit_cb_ctx, p)) { h = remove_history(where_history()); if (h) { - os_free(h->line); + free(h->line); free(h->data); - os_free(h); + free(h); } else next_history(); } else diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c index a095ea6abec5..13173cb19361 100644 --- a/src/utils/edit_simple.c +++ b/src/utils/edit_simple.c @@ -13,7 +13,7 @@ #include "edit.h" -#define CMD_BUF_LEN 256 +#define CMD_BUF_LEN 4096 static char cmdbuf[CMD_BUF_LEN]; static int cmdbuf_pos = 0; static const char *ps2 = NULL; diff --git a/src/utils/eloop.c b/src/utils/eloop.c index d01ae64f8a62..4a565ebdbd0a 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -7,17 +7,28 @@ */ #include "includes.h" +#include #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 #include #endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_EPOLL +#include +#endif /* CONFIG_ELOOP_EPOLL */ struct eloop_sock { int sock; @@ -31,7 +42,7 @@ struct eloop_sock { struct eloop_timeout { struct dl_list list; - struct os_time time; + struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; @@ -50,7 +61,11 @@ struct eloop_signal { struct eloop_sock_table { int count; struct eloop_sock *table; +#ifdef CONFIG_ELOOP_EPOLL + eloop_event_type type; +#else /* CONFIG_ELOOP_EPOLL */ int changed; +#endif /* CONFIG_ELOOP_EPOLL */ }; struct eloop_data { @@ -63,6 +78,13 @@ struct eloop_data { 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; @@ -75,7 +97,6 @@ struct eloop_data { int pending_terminate; int terminate; - int reader_table_changed; }; static struct eloop_data eloop; @@ -128,6 +149,17 @@ 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 */ @@ -139,6 +171,11 @@ 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; @@ -174,12 +211,41 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, 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) + if (tmp == NULL) { + eloop_trace_sock_add_ref(table); return -1; + } tmp[table->count].sock = sock; tmp[table->count].eloop_data = eloop_data; @@ -190,9 +256,38 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, table->table = tmp; eloop.max_sock = new_max_sock; eloop.count++; +#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; +#endif /* CONFIG_ELOOP_EPOLL */ 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; } @@ -219,8 +314,18 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } table->count--; eloop.count--; +#ifndef CONFIG_ELOOP_EPOLL table->changed = 1; +#endif /* CONFIG_ELOOP_EPOLL */ 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 */ } @@ -362,7 +467,9 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, max_pollfd_map, POLLERR | POLLHUP); } -#else /* CONFIG_ELOOP_POLL */ +#endif /* CONFIG_ELOOP_POLL */ + +#ifdef CONFIG_ELOOP_SELECT static void eloop_sock_table_set_fds(struct eloop_sock_table *table, fd_set *fds) @@ -374,8 +481,10 @@ static void eloop_sock_table_set_fds(struct eloop_sock_table *table, if (table->table == NULL) return; - for (i = 0; i < table->count; i++) + for (i = 0; i < table->count; i++) { + assert(table->table[i].sock >= 0); FD_SET(table->table[i].sock, fds); + } } @@ -399,7 +508,24 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table, } } -#endif /* CONFIG_ELOOP_POLL */ +#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); + } +} +#endif /* CONFIG_ELOOP_EPOLL */ static void eloop_sock_table_destroy(struct eloop_sock_table *table) @@ -459,6 +585,7 @@ int eloop_register_sock(int sock, eloop_event_type type, { 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); @@ -484,7 +611,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; - if (os_get_time(&timeout->time) < 0) { + if (os_get_reltime(&timeout->time) < 0) { os_free(timeout); return -1; } @@ -514,7 +641,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, /* Maintain timeouts in order of increasing time */ dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { - if (os_time_before(&timeout->time, &tmp->time)) { + if (os_reltime_before(&timeout->time, &tmp->time)) { dl_list_add(tmp->list.prev, &timeout->list); return 0; } @@ -556,6 +683,33 @@ int eloop_cancel_timeout(eloop_timeout_handler handler, } +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) { @@ -572,6 +726,70 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler, } +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) { @@ -682,20 +900,24 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_POLL int num_poll_fds; int timeout_ms = 0; -#else /* CONFIG_ELOOP_POLL */ +#endif /* CONFIG_ELOOP_POLL */ +#ifdef CONFIG_ELOOP_SELECT fd_set *rfds, *wfds, *efds; struct timeval _tv; -#endif /* CONFIG_ELOOP_POLL */ +#endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_EPOLL + int timeout_ms = -1; +#endif /* CONFIG_ELOOP_EPOLL */ int res; - struct os_time tv, now; + struct os_reltime tv, now; -#ifndef CONFIG_ELOOP_POLL +#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_POLL */ +#endif /* CONFIG_ELOOP_SELECT */ while (!eloop.terminate && (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || @@ -704,17 +926,18 @@ void eloop_run(void) timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); if (timeout) { - os_get_time(&now); - if (os_time_before(&now, &timeout->time)) - os_time_sub(&timeout->time, &now, &tv); + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); else tv.sec = tv.usec = 0; -#ifdef CONFIG_ELOOP_POLL +#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) timeout_ms = tv.sec * 1000 + tv.usec / 1000; -#else /* CONFIG_ELOOP_POLL */ +#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_POLL */ +#endif /* CONFIG_ELOOP_SELECT */ } #ifdef CONFIG_ELOOP_POLL @@ -724,30 +947,44 @@ void eloop_run(void) eloop.max_pollfd_map); res = poll(eloop.pollfds, num_poll_fds, timeout ? timeout_ms : -1); - - if (res < 0 && errno != EINTR && errno != 0) { - perror("poll"); - goto out; - } -#else /* CONFIG_ELOOP_POLL */ +#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) { - perror("select"); + 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; } -#endif /* CONFIG_ELOOP_POLL */ 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_time(&now); - if (!os_time_before(&now, &timeout->time)) { + 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 = @@ -765,19 +1002,24 @@ void eloop_run(void) eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, &eloop.exceptions, eloop.pollfds_map, eloop.max_pollfd_map); -#else /* CONFIG_ELOOP_POLL */ +#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_POLL */ +#endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_EPOLL + eloop_sock_table_dispatch(eloop.epoll_events, res); +#endif /* CONFIG_ELOOP_EPOLL */ } + eloop.terminate = 0; out: -#ifndef CONFIG_ELOOP_POLL +#ifdef CONFIG_ELOOP_SELECT os_free(rfds); os_free(wfds); os_free(efds); -#endif /* CONFIG_ELOOP_POLL */ +#endif /* CONFIG_ELOOP_SELECT */ return; } @@ -791,9 +1033,9 @@ void eloop_terminate(void) void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(timeout, prev, &eloop.timeout, struct eloop_timeout, list) { int sec, usec; @@ -821,6 +1063,11 @@ void eloop_destroy(void) 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 */ } @@ -843,7 +1090,13 @@ void eloop_wait_for_read_sock(int sock) pfd.events = POLLIN; poll(&pfd, 1, -1); -#else /* CONFIG_ELOOP_POLL */ +#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) @@ -852,5 +1105,9 @@ void eloop_wait_for_read_sock(int sock) FD_ZERO(&rfds); FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); -#endif /* CONFIG_ELOOP_POLL */ +#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */ } + +#ifdef CONFIG_ELOOP_SELECT +#undef CONFIG_ELOOP_SELECT +#endif /* CONFIG_ELOOP_SELECT */ diff --git a/src/utils/eloop.h b/src/utils/eloop.h index db03a735596f..07b8c0dc3352 100644 --- a/src/utils/eloop.h +++ b/src/utils/eloop.h @@ -194,6 +194,21 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, 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 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 @@ -207,6 +222,40 @@ int eloop_cancel_timeout(eloop_timeout_handler handler, 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 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 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) diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c deleted file mode 100644 index c67ece4d715e..000000000000 --- a/src/utils/eloop_none.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Event loop - empty template (basic structure, but no OS specific operations) - * Copyright (c) 2002-2005, Jouni Malinen - * - * 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" - - -struct eloop_sock { - int sock; - void *eloop_data; - void *user_data; - void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); -}; - -struct eloop_timeout { - struct os_time time; - void *eloop_data; - void *user_data; - void (*handler)(void *eloop_ctx, void *sock_ctx); - struct eloop_timeout *next; -}; - -struct eloop_signal { - int sig; - void *user_data; - void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); - int signaled; -}; - -struct eloop_data { - int max_sock, reader_count; - struct eloop_sock *readers; - - struct eloop_timeout *timeout; - - int signal_count; - struct eloop_signal *signals; - int signaled; - int pending_terminate; - - int terminate; - int reader_table_changed; -}; - -static struct eloop_data eloop; - - -int eloop_init(void) -{ - memset(&eloop, 0, sizeof(eloop)); - return 0; -} - - -int eloop_register_read_sock(int sock, - void (*handler)(int sock, void *eloop_ctx, - void *sock_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_sock *tmp; - - tmp = (struct eloop_sock *) - realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); - if (tmp == NULL) - return -1; - - tmp[eloop.reader_count].sock = sock; - tmp[eloop.reader_count].eloop_data = eloop_data; - tmp[eloop.reader_count].user_data = user_data; - tmp[eloop.reader_count].handler = handler; - eloop.reader_count++; - eloop.readers = tmp; - if (sock > eloop.max_sock) - eloop.max_sock = sock; - eloop.reader_table_changed = 1; - - return 0; -} - - -void eloop_unregister_read_sock(int sock) -{ - int i; - - if (eloop.readers == NULL || eloop.reader_count == 0) - return; - - for (i = 0; i < eloop.reader_count; i++) { - if (eloop.readers[i].sock == sock) - break; - } - if (i == eloop.reader_count) - return; - if (i != eloop.reader_count - 1) { - memmove(&eloop.readers[i], &eloop.readers[i + 1], - (eloop.reader_count - i - 1) * - sizeof(struct eloop_sock)); - } - eloop.reader_count--; - eloop.reader_table_changed = 1; -} - - -int eloop_register_timeout(unsigned int secs, unsigned int usecs, - void (*handler)(void *eloop_ctx, void *timeout_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *timeout, *tmp, *prev; - - timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); - if (timeout == NULL) - return -1; - os_get_time(&timeout->time); - timeout->time.sec += secs; - 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; - timeout->next = NULL; - - if (eloop.timeout == NULL) { - eloop.timeout = timeout; - return 0; - } - - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } - - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - - return 0; -} - - -int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *timeout, *prev, *next; - int removed = 0; - - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - - if (timeout->handler == handler && - (timeout->eloop_data == eloop_data || - eloop_data == ELOOP_ALL_CTX) && - (timeout->user_data == user_data || - user_data == ELOOP_ALL_CTX)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - free(timeout); - removed++; - } else - prev = timeout; - - timeout = next; - } - - return removed; -} - - -int eloop_is_timeout_registered(void (*handler)(void *eloop_ctx, - void *timeout_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *tmp; - - tmp = eloop.timeout; - while (tmp != NULL) { - if (tmp->handler == handler && - tmp->eloop_data == eloop_data && - tmp->user_data == user_data) - return 1; - - tmp = tmp->next; - } - - return 0; -} - - -/* TODO: replace with suitable signal handler */ -#if 0 -static void eloop_handle_signal(int sig) -{ - int i; - - eloop.signaled++; - for (i = 0; i < eloop.signal_count; i++) { - if (eloop.signals[i].sig == sig) { - eloop.signals[i].signaled++; - break; - } - } -} -#endif - - -static void eloop_process_pending_signals(void) -{ - int i; - - if (eloop.signaled == 0) - return; - eloop.signaled = 0; - - if (eloop.pending_terminate) { - 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.user_data, - eloop.signals[i].user_data); - } - } -} - - -int eloop_register_signal(int sig, - void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ - struct eloop_signal *tmp; - - tmp = (struct eloop_signal *) - realloc(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; - - /* TODO: register signal handler */ - - return 0; -} - - -int eloop_register_signal_terminate(void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ -#if 0 - /* TODO: for example */ - int ret = eloop_register_signal(SIGINT, handler, user_data); - if (ret == 0) - ret = eloop_register_signal(SIGTERM, handler, user_data); - return ret; -#endif - return 0; -} - - -int eloop_register_signal_reconfig(void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ -#if 0 - /* TODO: for example */ - return eloop_register_signal(SIGHUP, handler, user_data); -#endif - return 0; -} - - -void eloop_run(void) -{ - int i; - struct os_time tv, now; - - while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0)) { - if (eloop.timeout) { - os_get_time(&now); - if (os_time_before(&now, &eloop.timeout->time)) - os_time_sub(&eloop.timeout->time, &now, &tv); - else - tv.sec = tv.usec = 0; - } - - /* - * TODO: wait for any event (read socket ready, timeout (tv), - * signal - */ - os_sleep(1, 0); /* just a dummy wait for testing */ - - eloop_process_pending_signals(); - - /* check if some registered timeouts have occurred */ - if (eloop.timeout) { - struct eloop_timeout *tmp; - - os_get_time(&now); - if (!os_time_before(&now, &eloop.timeout->time)) { - tmp = eloop.timeout; - eloop.timeout = eloop.timeout->next; - tmp->handler(tmp->eloop_data, - tmp->user_data); - free(tmp); - } - - } - - eloop.reader_table_changed = 0; - for (i = 0; i < eloop.reader_count; i++) { - /* - * TODO: call each handler that has pending data to - * read - */ - if (0 /* TODO: eloop.readers[i].sock ready */) { - eloop.readers[i].handler( - eloop.readers[i].sock, - eloop.readers[i].eloop_data, - eloop.readers[i].user_data); - if (eloop.reader_table_changed) - break; - } - } - } -} - - -void eloop_terminate(void) -{ - eloop.terminate = 1; -} - - -void eloop_destroy(void) -{ - struct eloop_timeout *timeout, *prev; - - timeout = eloop.timeout; - while (timeout != NULL) { - prev = timeout; - timeout = timeout->next; - free(prev); - } - free(eloop.readers); - free(eloop.signals); -} - - -int eloop_terminated(void) -{ - return eloop.terminate; -} - - -void eloop_wait_for_read_sock(int sock) -{ - /* - * TODO: wait for the file descriptor to have something available for - * reading - */ -} diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c index 1fafeb2d9814..de47fb21837c 100644 --- a/src/utils/eloop_win.c +++ b/src/utils/eloop_win.c @@ -1,6 +1,6 @@ /* * Event loop based on Windows events and WaitForMultipleObjects - * Copyright (c) 2002-2006, Jouni Malinen + * Copyright (c) 2002-2009, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #include #include "common.h" +#include "list.h" #include "eloop.h" @@ -29,11 +30,11 @@ struct eloop_event { }; struct eloop_timeout { - struct os_time time; + struct dl_list list; + struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; - struct eloop_timeout *next; }; struct eloop_signal { @@ -51,7 +52,7 @@ struct eloop_data { size_t event_count; struct eloop_event *events; - struct eloop_timeout *timeout; + struct dl_list timeout; int signal_count; struct eloop_signal *signals; @@ -74,6 +75,7 @@ static struct eloop_data eloop; int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); + dl_list_init(&eloop.timeout); eloop.num_handles = 1; eloop.handles = os_malloc(eloop.num_handles * sizeof(eloop.handles[0])); @@ -236,13 +238,16 @@ 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, *prev; + struct eloop_timeout *timeout, *tmp; os_time_t now_sec; - timeout = os_malloc(sizeof(*timeout)); + timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; - os_get_time(&timeout->time); + 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) { @@ -263,85 +268,156 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; - timeout->next = NULL; - if (eloop.timeout == NULL) { - eloop.timeout = timeout; - return 0; + /* 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); - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } + return 0; +} - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - return 0; +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + dl_list_del(&timeout->list); + os_free(timeout); } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { - struct eloop_timeout *timeout, *prev, *next; + struct eloop_timeout *timeout, *prev; int removed = 0; - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - + 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)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - os_free(timeout); + eloop_remove_timeout(timeout); removed++; - } else - prev = timeout; - - timeout = next; + } } 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; - tmp = eloop.timeout; - while (tmp != NULL) { + 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; - - tmp = tmp->next; } 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; +} + + /* TODO: replace with suitable signal handler */ #if 0 static void eloop_handle_signal(int sig) @@ -456,18 +532,21 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler, void eloop_run(void) { - struct os_time tv, now; - DWORD count, ret, timeout, err; + struct os_reltime tv, now; + DWORD count, ret, timeout_val, err; size_t i; while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0 || + (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 || eloop.event_count > 0)) { + struct eloop_timeout *timeout; tv.sec = tv.usec = 0; - if (eloop.timeout) { - os_get_time(&now); - if (os_time_before(&now, &eloop.timeout->time)) - os_time_sub(&eloop.timeout->time, &now, &tv); + 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); } count = 0; @@ -480,10 +559,10 @@ void eloop_run(void) if (eloop.term_event) eloop.handles[count++] = eloop.term_event; - if (eloop.timeout) - timeout = tv.sec * 1000 + tv.usec / 1000; + if (timeout) + timeout_val = tv.sec * 1000 + tv.usec / 1000; else - timeout = INFINITE; + timeout_val = INFINITE; if (count > MAXIMUM_WAIT_OBJECTS) { printf("WaitForMultipleObjects: Too many events: " @@ -493,26 +572,27 @@ void eloop_run(void) } #ifdef _WIN32_WCE ret = WaitForMultipleObjects(count, eloop.handles, FALSE, - timeout); + timeout_val); #else /* _WIN32_WCE */ ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, - timeout, TRUE); + timeout_val, TRUE); #endif /* _WIN32_WCE */ err = GetLastError(); eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ - if (eloop.timeout) { - struct eloop_timeout *tmp; - - os_get_time(&now); - if (!os_time_before(&now, &eloop.timeout->time)) { - tmp = eloop.timeout; - eloop.timeout = eloop.timeout->next; - tmp->handler(tmp->eloop_data, - tmp->user_data); - os_free(tmp); + 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); } } @@ -571,11 +651,9 @@ void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; - timeout = eloop.timeout; - while (timeout != NULL) { - prev = timeout; - timeout = timeout->next; - os_free(prev); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + eloop_remove_timeout(timeout); } os_free(eloop.readers); os_free(eloop.signals); diff --git a/src/utils/ext_password_test.c b/src/utils/ext_password_test.c index 3801bb85449f..b3a4552d1c2c 100644 --- a/src/utils/ext_password_test.c +++ b/src/utils/ext_password_test.c @@ -36,7 +36,7 @@ static void ext_password_test_deinit(void *ctx) { struct ext_password_test_data *data = ctx; - os_free(data->params); + str_clear_free(data->params); os_free(data); } diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h new file mode 100644 index 000000000000..8d4399a37240 --- /dev/null +++ b/src/utils/http-utils.h @@ -0,0 +1,63 @@ +/* + * HTTP wrapper + * Copyright (c) 2012-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HTTP_UTILS_H +#define HTTP_UTILS_H + +struct http_ctx; + +struct http_othername { + char *oid; + u8 *data; + size_t len; +}; + +#define HTTP_MAX_CERT_LOGO_HASH 32 + +struct http_logo { + char *alg_oid; + u8 *hash; + size_t hash_len; + char *uri; +}; + +struct http_cert { + char **dnsname; + unsigned int num_dnsname; + struct http_othername *othername; + unsigned int num_othername; + struct http_logo *logo; + unsigned int num_logo; +}; + +int soap_init_client(struct http_ctx *ctx, const char *address, + const char *ca_fname, const char *username, + const char *password, const char *client_cert, + const char *client_key); +int soap_reinit_client(struct http_ctx *ctx); +xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node); + +struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx); +void http_ocsp_set(struct http_ctx *ctx, int val); +void http_deinit_ctx(struct http_ctx *ctx); + +int http_download_file(struct http_ctx *ctx, const char *url, + const char *fname, const char *ca_fname); +char * http_post(struct http_ctx *ctx, const char *url, const char *data, + const char *content_type, const char *ext_hdr, + const char *ca_fname, + const char *username, const char *password, + const char *client_cert, const char *client_key, + size_t *resp_len); +void http_set_cert_cb(struct http_ctx *ctx, + int (*cb)(void *ctx, struct http_cert *cert), + void *cb_ctx); +const char * http_get_err(struct http_ctx *ctx); +void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname); + +#endif /* HTTP_UTILS_H */ diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c new file mode 100644 index 000000000000..b38cf796ca2a --- /dev/null +++ b/src/utils/http_curl.c @@ -0,0 +1,1641 @@ +/* + * HTTP wrapper for libcurl + * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#ifdef EAP_TLS_OPENSSL +#include +#include +#include +#include + +#ifdef SSL_set_tlsext_status_type +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_OCSP +#include +#include +#endif /* OPENSSL_NO_TLSEXT */ +#endif /* SSL_set_tlsext_status_type */ +#endif /* EAP_TLS_OPENSSL */ + +#include "common.h" +#include "xml-utils.h" +#include "http-utils.h" + + +struct http_ctx { + void *ctx; + struct xml_node_ctx *xml; + CURL *curl; + struct curl_slist *curl_hdr; + char *svc_address; + char *svc_ca_fname; + char *svc_username; + char *svc_password; + char *svc_client_cert; + char *svc_client_key; + char *curl_buf; + size_t curl_buf_len; + + int (*cert_cb)(void *ctx, struct http_cert *cert); + void *cert_cb_ctx; + + enum { + NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP + } ocsp; + X509 *peer_cert; + X509 *peer_issuer; + X509 *peer_issuer_issuer; + + const char *last_err; +}; + + +static void clear_curl(struct http_ctx *ctx) +{ + if (ctx->curl) { + curl_easy_cleanup(ctx->curl); + ctx->curl = NULL; + } + if (ctx->curl_hdr) { + curl_slist_free_all(ctx->curl_hdr); + ctx->curl_hdr = NULL; + } +} + + +static void clone_str(char **dst, const char *src) +{ + os_free(*dst); + if (src) + *dst = os_strdup(src); + else + *dst = NULL; +} + + +static void debug_dump(struct http_ctx *ctx, const char *title, + const char *buf, size_t len) +{ + char *txt; + size_t i; + + for (i = 0; i < len; i++) { + if (buf[i] < 32 && buf[i] != '\t' && buf[i] != '\n' && + buf[i] != '\r') { + wpa_hexdump_ascii(MSG_MSGDUMP, title, buf, len); + return; + } + } + + txt = os_malloc(len + 1); + if (txt == NULL) + return; + os_memcpy(txt, buf, len); + txt[len] = '\0'; + while (len > 0) { + len--; + if (txt[len] == '\n' || txt[len] == '\r') + txt[len] = '\0'; + else + break; + } + wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt); + os_free(txt); +} + + +static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len, + void *userdata) +{ + struct http_ctx *ctx = userdata; + switch (info) { + case CURLINFO_TEXT: + debug_dump(ctx, "CURLINFO_TEXT", buf, len); + break; + case CURLINFO_HEADER_IN: + debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len); + break; + case CURLINFO_HEADER_OUT: + debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len); + break; + case CURLINFO_DATA_IN: + debug_dump(ctx, "CURLINFO_DATA_IN", buf, len); + break; + case CURLINFO_DATA_OUT: + debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len); + break; + case CURLINFO_SSL_DATA_IN: + wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d", + (int) len); + break; + case CURLINFO_SSL_DATA_OUT: + wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d", + (int) len); + break; + case CURLINFO_END: + wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d", + (int) len); + break; + } + return 0; +} + + +static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb, + void *userdata) +{ + struct http_ctx *ctx = userdata; + char *n; + n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1); + if (n == NULL) + return 0; + ctx->curl_buf = n; + os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb); + n[ctx->curl_buf_len + size * nmemb] = '\0'; + ctx->curl_buf_len += size * nmemb; + return size * nmemb; +} + + +#ifdef EAP_TLS_OPENSSL + +static void debug_dump_cert(const char *title, X509 *cert) +{ + BIO *out; + char *txt; + size_t rlen; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (txt) { + int res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt); + } + os_free(txt); + } + BIO_free(out); +} + + +static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert, + OTHERNAME *o) +{ + char txt[100]; + int res; + struct http_othername *on; + ASN1_TYPE *val; + + on = os_realloc_array(cert->othername, cert->num_othername + 1, + sizeof(struct http_othername)); + if (on == NULL) + return; + cert->othername = on; + on = &on[cert->num_othername]; + os_memset(on, 0, sizeof(*on)); + + res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1); + if (res < 0 || res >= (int) sizeof(txt)) + return; + + on->oid = os_strdup(txt); + if (on->oid == NULL) + return; + + val = o->value; + on->data = val->value.octet_string->data; + on->len = val->value.octet_string->length; + + cert->num_othername++; +} + + +static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert, + ASN1_STRING *name) +{ + char *buf; + char **n; + + buf = NULL; + if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0) + return; + + n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1, + sizeof(char *)); + if (n == NULL) + return; + + cert->dnsname = n; + n[cert->num_dnsname] = buf; + cert->num_dnsname++; +} + + +static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert, + const GENERAL_NAME *name) +{ + switch (name->type) { + case GEN_OTHERNAME: + add_alt_name_othername(ctx, cert, name->d.otherName); + break; + case GEN_DNS: + add_alt_name_dns(ctx, cert, name->d.dNSName); + break; + } +} + + +static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert, + GENERAL_NAMES *names) +{ + int num, i; + + num = sk_GENERAL_NAME_num(names); + for (i = 0; i < num; i++) { + const GENERAL_NAME *name; + name = sk_GENERAL_NAME_value(names, i); + add_alt_name(ctx, cert, name); + } +} + + +/* RFC 3709 */ + +typedef struct { + X509_ALGOR *hashAlg; + ASN1_OCTET_STRING *hashValue; +} HashAlgAndValue; + +typedef struct { + STACK_OF(HashAlgAndValue) *refStructHash; + STACK_OF(ASN1_IA5STRING) *refStructURI; +} LogotypeReference; + +typedef struct { + ASN1_IA5STRING *mediaType; + STACK_OF(HashAlgAndValue) *logotypeHash; + STACK_OF(ASN1_IA5STRING) *logotypeURI; +} LogotypeDetails; + +typedef struct { + int type; + union { + ASN1_INTEGER *numBits; + ASN1_INTEGER *tableSize; + } d; +} LogotypeImageResolution; + +typedef struct { + ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */ + ASN1_INTEGER *fileSize; + ASN1_INTEGER *xSize; + ASN1_INTEGER *ySize; + LogotypeImageResolution *resolution; + ASN1_IA5STRING *language; +} LogotypeImageInfo; + +typedef struct { + LogotypeDetails *imageDetails; + LogotypeImageInfo *imageInfo; +} LogotypeImage; + +typedef struct { + ASN1_INTEGER *fileSize; + ASN1_INTEGER *playTime; + ASN1_INTEGER *channels; + ASN1_INTEGER *sampleRate; + ASN1_IA5STRING *language; +} LogotypeAudioInfo; + +typedef struct { + LogotypeDetails *audioDetails; + LogotypeAudioInfo *audioInfo; +} LogotypeAudio; + +typedef struct { + STACK_OF(LogotypeImage) *image; + STACK_OF(LogotypeAudio) *audio; +} LogotypeData; + +typedef struct { + int type; + union { + LogotypeData *direct; + LogotypeReference *indirect; + } d; +} LogotypeInfo; + +typedef struct { + ASN1_OBJECT *logotypeType; + LogotypeInfo *info; +} OtherLogotypeInfo; + +typedef struct { + STACK_OF(LogotypeInfo) *communityLogos; + LogotypeInfo *issuerLogo; + LogotypeInfo *subjectLogo; + STACK_OF(OtherLogotypeInfo) *otherLogos; +} LogotypeExtn; + +ASN1_SEQUENCE(HashAlgAndValue) = { + ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR), + ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(HashAlgAndValue); + +ASN1_SEQUENCE(LogotypeReference) = { + ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue), + ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING) +} ASN1_SEQUENCE_END(LogotypeReference); + +ASN1_SEQUENCE(LogotypeDetails) = { + ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING), + ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue), + ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING) +} ASN1_SEQUENCE_END(LogotypeDetails); + +ASN1_CHOICE(LogotypeImageResolution) = { + ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1), + ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2) +} ASN1_CHOICE_END(LogotypeImageResolution); + +ASN1_SEQUENCE(LogotypeImageInfo) = { + ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0), + ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER), + ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER), + ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER), + ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution), + ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4), +} ASN1_SEQUENCE_END(LogotypeImageInfo); + +ASN1_SEQUENCE(LogotypeImage) = { + ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails), + ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo) +} ASN1_SEQUENCE_END(LogotypeImage); + +ASN1_SEQUENCE(LogotypeAudioInfo) = { + ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER), + ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER), + ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER), + ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3), + ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4) +} ASN1_SEQUENCE_END(LogotypeAudioInfo); + +ASN1_SEQUENCE(LogotypeAudio) = { + ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails), + ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo) +} ASN1_SEQUENCE_END(LogotypeAudio); + +ASN1_SEQUENCE(LogotypeData) = { + ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage), + ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1) +} ASN1_SEQUENCE_END(LogotypeData); + +ASN1_CHOICE(LogotypeInfo) = { + ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0), + ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1) +} ASN1_CHOICE_END(LogotypeInfo); + +ASN1_SEQUENCE(OtherLogotypeInfo) = { + ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT), + ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo) +} ASN1_SEQUENCE_END(OtherLogotypeInfo); + +ASN1_SEQUENCE(LogotypeExtn) = { + ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0), + ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1), + ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2), + ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3) +} ASN1_SEQUENCE_END(LogotypeExtn); + +IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); + +#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st)) +#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i)) +#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st)) +#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i)) +#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st)) +#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i)) +#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st)) +#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i)) +#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) +#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) + + +static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, + HashAlgAndValue *hash, ASN1_IA5STRING *uri) +{ + char txt[100]; + int res, len; + struct http_logo *n; + + if (hash == NULL || uri == NULL) + return; + + res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1); + if (res < 0 || res >= (int) sizeof(txt)) + return; + + n = os_realloc_array(hcert->logo, hcert->num_logo + 1, + sizeof(struct http_logo)); + if (n == NULL) + return; + hcert->logo = n; + n = &hcert->logo[hcert->num_logo]; + os_memset(n, 0, sizeof(*n)); + + n->alg_oid = os_strdup(txt); + if (n->alg_oid == NULL) + return; + + n->hash_len = ASN1_STRING_length(hash->hashValue); + n->hash = os_malloc(n->hash_len); + if (n->hash == NULL) { + os_free(n->alg_oid); + return; + } + os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len); + + len = ASN1_STRING_length(uri); + n->uri = os_malloc(len + 1); + if (n->uri == NULL) { + os_free(n->alg_oid); + os_free(n->hash); + return; + } + os_memcpy(n->uri, ASN1_STRING_data(uri), len); + n->uri[len] = '\0'; + + hcert->num_logo++; +} + + +static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert, + LogotypeData *data) +{ + int i, num; + + if (data->image == NULL) + return; + + num = sk_LogotypeImage_num(data->image); + for (i = 0; i < num; i++) { + LogotypeImage *image; + LogotypeDetails *details; + int j, hash_num, uri_num; + HashAlgAndValue *found_hash = NULL; + + image = sk_LogotypeImage_value(data->image, i); + if (image == NULL) + continue; + + details = image->imageDetails; + if (details == NULL) + continue; + + hash_num = sk_HashAlgAndValue_num(details->logotypeHash); + for (j = 0; j < hash_num; j++) { + HashAlgAndValue *hash; + char txt[100]; + int res; + hash = sk_HashAlgAndValue_value(details->logotypeHash, + j); + if (hash == NULL) + continue; + res = OBJ_obj2txt(txt, sizeof(txt), + hash->hashAlg->algorithm, 1); + if (res < 0 || res >= (int) sizeof(txt)) + continue; + if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) { + found_hash = hash; + break; + } + } + + if (!found_hash) { + wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo"); + continue; + } + + uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI); + for (j = 0; j < uri_num; j++) { + ASN1_IA5STRING *uri; + uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j); + add_logo(ctx, hcert, found_hash, uri); + } + } +} + + +static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert, + LogotypeReference *ref) +{ + int j, hash_num, uri_num; + + hash_num = sk_HashAlgAndValue_num(ref->refStructHash); + uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI); + if (hash_num != uri_num) { + wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d", + hash_num, uri_num); + return; + } + + for (j = 0; j < hash_num; j++) { + HashAlgAndValue *hash; + ASN1_IA5STRING *uri; + hash = sk_HashAlgAndValue_value(ref->refStructHash, j); + uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j); + add_logo(ctx, hcert, hash, uri); + } +} + + +static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent) +{ + int i; + const unsigned char *data; + + BIO_printf(out, "%*shashAlg: ", indent, ""); + i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm); + BIO_printf(out, "\n"); + + BIO_printf(out, "%*shashValue: ", indent, ""); + data = hash->hashValue->data; + for (i = 0; i < hash->hashValue->length; i++) + BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]); + BIO_printf(out, "\n"); +} + +static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent) +{ + int i, num; + + BIO_printf(out, "%*sLogotypeDetails\n", indent, ""); + if (details->mediaType) { + BIO_printf(out, "%*smediaType: ", indent, ""); + ASN1_STRING_print(out, details->mediaType); + BIO_printf(out, "\n"); + } + + num = details->logotypeHash ? + sk_HashAlgAndValue_num(details->logotypeHash) : 0; + for (i = 0; i < num; i++) { + HashAlgAndValue *hash; + hash = sk_HashAlgAndValue_value(details->logotypeHash, i); + i2r_HashAlgAndValue(hash, out, indent); + } + + num = details->logotypeURI ? + sk_ASN1_IA5STRING_num(details->logotypeURI) : 0; + for (i = 0; i < num; i++) { + ASN1_IA5STRING *uri; + uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i); + BIO_printf(out, "%*slogotypeURI: ", indent, ""); + ASN1_STRING_print(out, uri); + BIO_printf(out, "\n"); + } +} + +static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent) +{ + long val; + + BIO_printf(out, "%*sLogotypeImageInfo\n", indent, ""); + if (info->type) { + val = ASN1_INTEGER_get(info->type); + BIO_printf(out, "%*stype: %ld\n", indent, "", val); + } else { + BIO_printf(out, "%*stype: default (1)\n", indent, ""); + } + val = ASN1_INTEGER_get(info->xSize); + BIO_printf(out, "%*sxSize: %ld\n", indent, "", val); + val = ASN1_INTEGER_get(info->ySize); + BIO_printf(out, "%*sySize: %ld\n", indent, "", val); + if (info->resolution) { + BIO_printf(out, "%*sresolution\n", indent, ""); + /* TODO */ + } + if (info->language) { + BIO_printf(out, "%*slanguage: ", indent, ""); + ASN1_STRING_print(out, info->language); + BIO_printf(out, "\n"); + } +} + +static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent) +{ + BIO_printf(out, "%*sLogotypeImage\n", indent, ""); + if (image->imageDetails) { + i2r_LogotypeDetails(image->imageDetails, out, indent + 4); + } + if (image->imageInfo) { + i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4); + } +} + +static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out, + int indent) +{ + int i, num; + + BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title); + + num = data->image ? sk_LogotypeImage_num(data->image) : 0; + for (i = 0; i < num; i++) { + LogotypeImage *image = sk_LogotypeImage_value(data->image, i); + i2r_LogotypeImage(image, out, indent + 4); + } + + num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0; + for (i = 0; i < num; i++) { + BIO_printf(out, "%*saudio: TODO\n", indent, ""); + } +} + +static void i2r_LogotypeReference(LogotypeReference *ref, const char *title, + BIO *out, int indent) +{ + int i, hash_num, uri_num; + + BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title); + + hash_num = ref->refStructHash ? + sk_HashAlgAndValue_num(ref->refStructHash) : 0; + uri_num = ref->refStructURI ? + sk_ASN1_IA5STRING_num(ref->refStructURI) : 0; + if (hash_num != uri_num) { + BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n", + indent, "", hash_num, uri_num); + return; + } + + for (i = 0; i < hash_num; i++) { + HashAlgAndValue *hash; + ASN1_IA5STRING *uri; + + hash = sk_HashAlgAndValue_value(ref->refStructHash, i); + i2r_HashAlgAndValue(hash, out, indent); + + uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i); + BIO_printf(out, "%*srefStructURI: ", indent, ""); + ASN1_STRING_print(out, uri); + BIO_printf(out, "\n"); + } +} + +static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out, + int indent) +{ + switch (info->type) { + case 0: + i2r_LogotypeData(info->d.direct, title, out, indent); + break; + case 1: + i2r_LogotypeReference(info->d.indirect, title, out, indent); + break; + } +} + +static void debug_print_logotypeext(LogotypeExtn *logo) +{ + BIO *out; + int i, num; + int indent = 0; + + out = BIO_new_fp(stdout, BIO_NOCLOSE); + if (out == NULL) + return; + + if (logo->communityLogos) { + num = sk_LogotypeInfo_num(logo->communityLogos); + for (i = 0; i < num; i++) { + LogotypeInfo *info; + info = sk_LogotypeInfo_value(logo->communityLogos, i); + i2r_LogotypeInfo(info, "communityLogo", out, indent); + } + } + + if (logo->issuerLogo) { + i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent ); + } + + if (logo->subjectLogo) { + i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent); + } + + if (logo->otherLogos) { + BIO_printf(out, "%*sotherLogos - TODO\n", indent, ""); + } + + BIO_free(out); +} + + +static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert, + X509 *cert) +{ + ASN1_OBJECT *obj; + int pos; + X509_EXTENSION *ext; + ASN1_OCTET_STRING *os; + LogotypeExtn *logo; + const unsigned char *data; + int i, num; + + obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0); + if (obj == NULL) + return; + + pos = X509_get_ext_by_OBJ(cert, obj, -1); + if (pos < 0) { + wpa_printf(MSG_INFO, "No logotype extension included"); + return; + } + + wpa_printf(MSG_INFO, "Parsing logotype extension"); + ext = X509_get_ext(cert, pos); + if (!ext) { + wpa_printf(MSG_INFO, "Could not get logotype extension"); + return; + } + + os = X509_EXTENSION_get_data(ext); + if (os == NULL) { + wpa_printf(MSG_INFO, "Could not get logotype extension data"); + return; + } + + wpa_hexdump(MSG_DEBUG, "logotypeExtn", + ASN1_STRING_data(os), ASN1_STRING_length(os)); + + data = ASN1_STRING_data(os); + logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os)); + if (logo == NULL) { + wpa_printf(MSG_INFO, "Failed to parse logotypeExtn"); + return; + } + + if (wpa_debug_level < MSG_INFO) + debug_print_logotypeext(logo); + + if (!logo->communityLogos) { + wpa_printf(MSG_INFO, "No communityLogos included"); + LogotypeExtn_free(logo); + return; + } + + num = sk_LogotypeInfo_num(logo->communityLogos); + for (i = 0; i < num; i++) { + LogotypeInfo *info; + info = sk_LogotypeInfo_value(logo->communityLogos, i); + switch (info->type) { + case 0: + add_logo_direct(ctx, hcert, info->d.direct); + break; + case 1: + add_logo_indirect(ctx, hcert, info->d.indirect); + break; + } + } + + LogotypeExtn_free(logo); +} + + +static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert, + X509 *cert, GENERAL_NAMES **names) +{ + os_memset(hcert, 0, sizeof(*hcert)); + + *names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (*names) + add_alt_names(ctx, hcert, *names); + + add_logotype_ext(ctx, hcert, cert); +} + + +static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names) +{ + unsigned int i; + + for (i = 0; i < hcert->num_dnsname; i++) + OPENSSL_free(hcert->dnsname[i]); + os_free(hcert->dnsname); + + for (i = 0; i < hcert->num_othername; i++) + os_free(hcert->othername[i].oid); + os_free(hcert->othername); + + for (i = 0; i < hcert->num_logo; i++) { + os_free(hcert->logo[i].alg_oid); + os_free(hcert->logo[i].hash); + os_free(hcert->logo[i].uri); + } + os_free(hcert->logo); + + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); +} + + +static int validate_server_cert(struct http_ctx *ctx, X509 *cert) +{ + GENERAL_NAMES *names; + struct http_cert hcert; + int ret; + + if (ctx->cert_cb == NULL) + return 0; + + if (0) { + BIO *out; + out = BIO_new_fp(stdout, BIO_NOCLOSE); + X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); + BIO_free(out); + } + + parse_cert(ctx, &hcert, cert, &names); + ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert); + parse_cert_free(&hcert, names); + + return ret; +} + + +void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname) +{ + BIO *in, *out; + X509 *cert; + GENERAL_NAMES *names; + struct http_cert hcert; + unsigned int i; + + in = BIO_new_file(fname, "r"); + if (in == NULL) { + wpa_printf(MSG_ERROR, "Could not read '%s'", fname); + return; + } + + cert = d2i_X509_bio(in, NULL); + BIO_free(in); + + if (cert == NULL) { + wpa_printf(MSG_ERROR, "Could not parse certificate"); + return; + } + + out = BIO_new_fp(stdout, BIO_NOCLOSE); + if (out) { + X509_print_ex(out, cert, XN_FLAG_COMPAT, + X509_FLAG_COMPAT); + BIO_free(out); + } + + wpa_printf(MSG_INFO, "Additional parsing information:"); + parse_cert(ctx, &hcert, cert, &names); + for (i = 0; i < hcert.num_othername; i++) { + if (os_strcmp(hcert.othername[i].oid, + "1.3.6.1.4.1.40808.1.1.1") == 0) { + char *name = os_zalloc(hcert.othername[i].len + 1); + if (name) { + os_memcpy(name, hcert.othername[i].data, + hcert.othername[i].len); + wpa_printf(MSG_INFO, + "id-wfa-hotspot-friendlyName: %s", + name); + os_free(name); + } + wpa_hexdump_ascii(MSG_INFO, + "id-wfa-hotspot-friendlyName", + hcert.othername[i].data, + hcert.othername[i].len); + } else { + wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s", + hcert.othername[i].oid); + wpa_hexdump_ascii(MSG_INFO, "unknown othername", + hcert.othername[i].data, + hcert.othername[i].len); + } + } + parse_cert_free(&hcert, names); + + X509_free(cert); +} + + +static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + struct http_ctx *ctx; + X509 *cert; + int err, depth; + char buf[256]; + X509_NAME *name; + const char *err_str; + SSL *ssl; + SSL_CTX *ssl_ctx; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + ssl_ctx = ssl->ctx; + ctx = SSL_CTX_get_app_data(ssl_ctx); + + wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify"); + + err = X509_STORE_CTX_get_error(x509_ctx); + err_str = X509_verify_cert_error_string(err); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + cert = X509_STORE_CTX_get_current_cert(x509_ctx); + if (!cert) { + wpa_printf(MSG_INFO, "No server certificate available"); + ctx->last_err = "No server certificate available"; + return 0; + } + + if (depth == 0) + ctx->peer_cert = cert; + else if (depth == 1) + ctx->peer_issuer = cert; + else if (depth == 2) + ctx->peer_issuer_issuer = cert; + + name = X509_get_subject_name(cert); + X509_NAME_oneline(name, buf, sizeof(buf)); + wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s", + depth, err, err_str, buf); + debug_dump_cert("Server certificate chain - certificate", cert); + + if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0) + return 0; + + if (!preverify_ok) + ctx->last_err = "TLS validation failed"; + + return preverify_ok; +} + + +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ + BIO *out; + size_t rlen; + char *txt; + int res; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + OCSP_RESPONSE_print(out, rsp, 0); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt); + } + os_free(txt); + BIO_free(out); +} + + +static void tls_show_errors(const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + + +static int ocsp_resp_cb(SSL *s, void *arg) +{ + struct http_ctx *ctx = arg; + const unsigned char *p; + int len, status, reason; + OCSP_RESPONSE *rsp; + OCSP_BASICRESP *basic; + OCSP_CERTID *id; + ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; + X509_STORE *store; + STACK_OF(X509) *certs = NULL; + + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + if (!p) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + if (ctx->ocsp == MANDATORY_OCSP) + ctx->last_err = "No OCSP response received"; + return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); + ctx->last_err = "Failed to parse OCSP response"; + return 0; + } + + ocsp_debug_print_resp(rsp); + + status = OCSP_response_status(rsp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", + status, OCSP_response_status_str(status)); + ctx->last_err = "OCSP responder error"; + return 0; + } + + basic = OCSP_response_get1_basic(rsp); + if (!basic) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); + ctx->last_err = "Could not find BasicOCSPResponse"; + return 0; + } + + store = SSL_CTX_get_cert_store(s->ctx); + if (ctx->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); + debug_dump_cert("OpenSSL: Issuer certificate", + ctx->peer_issuer); + + if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) { + tls_show_errors(__func__, + "OpenSSL: Could not add issuer to certificate store"); + } + certs = sk_X509_new_null(); + if (certs) { + X509 *cert; + cert = X509_dup(ctx->peer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store"); + X509_free(cert); + sk_X509_free(certs); + certs = NULL; + } + if (certs && ctx->peer_issuer_issuer) { + cert = X509_dup(ctx->peer_issuer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + __func__, + "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); + X509_free(cert); + } + } + } + } + + status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); + sk_X509_pop_free(certs, X509_free); + if (status <= 0) { + tls_show_errors(__func__, + "OpenSSL: OCSP response failed verification"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = "OCSP response failed verification"; + return 0; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); + + if (!ctx->peer_cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = "Peer certificate not available for OCSP status check"; + return 0; + } + + if (!ctx->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = "Peer issuer certificate not available for OCSP status check"; + return 0; + } + + id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = "Could not create OCSP certificate identifier"; + return 0; + } + + if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update)) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", + (ctx->ocsp == MANDATORY_OCSP) ? "" : + " (OCSP not required)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + if (ctx->ocsp == MANDATORY_OCSP) + + ctx->last_err = "Could not find current server certificate from OCSP response"; + return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1; + } + + if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { + tls_show_errors(__func__, "OpenSSL: OCSP status times invalid"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = "OCSP status times invalid"; + return 0; + } + + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", + OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + return 1; + if (status == V_OCSP_CERTSTATUS_REVOKED) { + ctx->last_err = "Server certificate has been revoked"; + return 0; + } + if (ctx->ocsp == MANDATORY_OCSP) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); + ctx->last_err = "OCSP status unknown"; + return 0; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + return 1; +} + + +static SSL_METHOD patch_ssl_method; +static const SSL_METHOD *real_ssl_method; + +static int curl_patch_ssl_new(SSL *s) +{ + SSL_CTX *ssl = s->ctx; + int ret; + + ssl->method = real_ssl_method; + s->method = real_ssl_method; + + ret = s->method->ssl_new(s); + SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp); + + return ret; +} + +#endif /* HAVE_OCSP */ + + +static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm) +{ + struct http_ctx *ctx = parm; + SSL_CTX *ssl = sslctx; + + wpa_printf(MSG_DEBUG, "curl_cb_ssl"); + SSL_CTX_set_app_data(ssl, ctx); + SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify); + +#ifdef HAVE_OCSP + if (ctx->ocsp != NO_OCSP) { + SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ssl, ctx); + + /* + * Use a temporary SSL_METHOD to get a callback on SSL_new() + * from libcurl since there is no proper callback registration + * available for this. + */ + os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method)); + patch_ssl_method.ssl_new = curl_patch_ssl_new; + real_ssl_method = ssl->method; + ssl->method = &patch_ssl_method; + } +#endif /* HAVE_OCSP */ + + return CURLE_OK; +} + +#endif /* EAP_TLS_OPENSSL */ + + +static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, + const char *ca_fname, const char *username, + const char *password, const char *client_cert, + const char *client_key) +{ + CURL *curl; + + wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s " + "username=%s", address, ca_fname, username); + + curl = curl_easy_init(); + if (curl == NULL) + return NULL; + + curl_easy_setopt(curl, CURLOPT_URL, address); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + if (ca_fname) { + curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); +#ifdef EAP_TLS_OPENSSL + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl); + curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx); +#endif /* EAP_TLS_OPENSSL */ + } else { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + } + if (client_cert && client_key) { + curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert); + curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key); + } + /* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch + * information about the server certificate */ + curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if (username) { + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE); + curl_easy_setopt(curl, CURLOPT_USERNAME, username); + curl_easy_setopt(curl, CURLOPT_PASSWORD, password); + } + + return curl; +} + + +static int post_init_client(struct http_ctx *ctx, const char *address, + const char *ca_fname, const char *username, + const char *password, const char *client_cert, + const char *client_key) +{ + char *pos; + int count; + + clone_str(&ctx->svc_address, address); + clone_str(&ctx->svc_ca_fname, ca_fname); + clone_str(&ctx->svc_username, username); + clone_str(&ctx->svc_password, password); + clone_str(&ctx->svc_client_cert, client_cert); + clone_str(&ctx->svc_client_key, client_key); + + /* + * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname + * 'foo' provided via HTTP are different. + */ + for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos; + pos++) { + if (*pos == '/') + count++; + *pos = tolower(*pos); + } + + ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username, + password, client_cert, client_key); + if (ctx->curl == NULL) + return -1; + + return 0; +} + + +int soap_init_client(struct http_ctx *ctx, const char *address, + const char *ca_fname, const char *username, + const char *password, const char *client_cert, + const char *client_key) +{ + if (post_init_client(ctx, address, ca_fname, username, password, + client_cert, client_key) < 0) + return -1; + + ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, + "Content-Type: application/soap+xml"); + ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: "); + ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:"); + curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr); + + return 0; +} + + +int soap_reinit_client(struct http_ctx *ctx) +{ + char *address = NULL; + char *ca_fname = NULL; + char *username = NULL; + char *password = NULL; + char *client_cert = NULL; + char *client_key = NULL; + int ret; + + clear_curl(ctx); + + clone_str(&address, ctx->svc_address); + clone_str(&ca_fname, ctx->svc_ca_fname); + clone_str(&username, ctx->svc_username); + clone_str(&password, ctx->svc_password); + clone_str(&client_cert, ctx->svc_client_cert); + clone_str(&client_key, ctx->svc_client_key); + + ret = soap_init_client(ctx, address, ca_fname, username, password, + client_cert, client_key); + os_free(address); + os_free(ca_fname); + str_clear_free(username); + str_clear_free(password); + os_free(client_cert); + os_free(client_key); + return ret; +} + + +static void free_curl_buf(struct http_ctx *ctx) +{ + os_free(ctx->curl_buf); + ctx->curl_buf = NULL; + ctx->curl_buf_len = 0; +} + + +xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node) +{ + char *str; + xml_node_t *envelope, *ret, *resp, *n; + CURLcode res; + long http = 0; + + ctx->last_err = NULL; + + wpa_printf(MSG_DEBUG, "SOAP: Sending message"); + envelope = soap_build_envelope(ctx->xml, node); + str = xml_node_to_str(ctx->xml, envelope); + xml_node_free(ctx->xml, envelope); + wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str); + + curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str); + free_curl_buf(ctx); + + res = curl_easy_perform(ctx->curl); + if (res != CURLE_OK) { + if (!ctx->last_err) + ctx->last_err = curl_easy_strerror(res); + wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", + ctx->last_err); + os_free(str); + free_curl_buf(ctx); + return NULL; + } + os_free(str); + + curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http); + wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http); + if (http != 200) { + ctx->last_err = "HTTP download failed"; + wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http); + free_curl_buf(ctx); + return NULL; + } + + if (ctx->curl_buf == NULL) + return NULL; + + wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf); + resp = xml_node_from_buf(ctx->xml, ctx->curl_buf); + free_curl_buf(ctx); + if (resp == NULL) { + wpa_printf(MSG_INFO, "Could not parse SOAP response"); + ctx->last_err = "Could not parse SOAP response"; + return NULL; + } + + ret = soap_get_body(ctx->xml, resp); + if (ret == NULL) { + wpa_printf(MSG_INFO, "Could not get SOAP body"); + ctx->last_err = "Could not get SOAP body"; + return NULL; + } + + wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'", + xml_node_get_localname(ctx->xml, ret)); + n = xml_node_copy(ctx->xml, ret); + xml_node_free(ctx->xml, resp); + + return n; +} + + +struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx) +{ + struct http_ctx *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->ctx = upper_ctx; + ctx->xml = xml_ctx; + ctx->ocsp = OPTIONAL_OCSP; + + curl_global_init(CURL_GLOBAL_ALL); + + return ctx; +} + + +void http_ocsp_set(struct http_ctx *ctx, int val) +{ + if (val == 0) + ctx->ocsp = NO_OCSP; + else if (val == 1) + ctx->ocsp = OPTIONAL_OCSP; + if (val == 2) + ctx->ocsp = MANDATORY_OCSP; +} + + +void http_deinit_ctx(struct http_ctx *ctx) +{ + clear_curl(ctx); + os_free(ctx->curl_buf); + curl_global_cleanup(); + + os_free(ctx->svc_address); + os_free(ctx->svc_ca_fname); + str_clear_free(ctx->svc_username); + str_clear_free(ctx->svc_password); + os_free(ctx->svc_client_cert); + os_free(ctx->svc_client_key); + + os_free(ctx); +} + + +int http_download_file(struct http_ctx *ctx, const char *url, + const char *fname, const char *ca_fname) +{ + CURL *curl; + FILE *f; + CURLcode res; + long http = 0; + + ctx->last_err = NULL; + + wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)", + url, fname, ca_fname); + curl = curl_easy_init(); + if (curl == NULL) + return -1; + + f = fopen(fname, "wb"); + if (f == NULL) { + curl_easy_cleanup(curl); + return -1; + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + if (ca_fname) { + curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); + } else { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + } + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, f); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + if (!ctx->last_err) + ctx->last_err = curl_easy_strerror(res); + wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", + ctx->last_err); + curl_easy_cleanup(curl); + fclose(f); + return -1; + } + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http); + wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http); + if (http != 200) { + ctx->last_err = "HTTP download failed"; + wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http); + curl_easy_cleanup(curl); + fclose(f); + return -1; + } + + curl_easy_cleanup(curl); + fclose(f); + + return 0; +} + + +char * http_post(struct http_ctx *ctx, const char *url, const char *data, + const char *content_type, const char *ext_hdr, + const char *ca_fname, + const char *username, const char *password, + const char *client_cert, const char *client_key, + size_t *resp_len) +{ + long http = 0; + CURLcode res; + char *ret; + CURL *curl; + struct curl_slist *curl_hdr = NULL; + + ctx->last_err = NULL; + wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url); + curl = setup_curl_post(ctx, url, ca_fname, username, password, + client_cert, client_key); + if (curl == NULL) + return NULL; + + if (content_type) { + char ct[200]; + snprintf(ct, sizeof(ct), "Content-Type: %s", content_type); + curl_hdr = curl_slist_append(curl_hdr, ct); + } + if (ext_hdr) + curl_hdr = curl_slist_append(curl_hdr, ext_hdr); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + free_curl_buf(ctx); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + if (!ctx->last_err) + ctx->last_err = curl_easy_strerror(res); + wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", + ctx->last_err); + free_curl_buf(ctx); + return NULL; + } + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http); + wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http); + if (http != 200) { + ctx->last_err = "HTTP POST failed"; + wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http); + free_curl_buf(ctx); + return NULL; + } + + if (ctx->curl_buf == NULL) + return NULL; + + ret = ctx->curl_buf; + if (resp_len) + *resp_len = ctx->curl_buf_len; + ctx->curl_buf = NULL; + ctx->curl_buf_len = 0; + + wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret); + + return ret; +} + + +void http_set_cert_cb(struct http_ctx *ctx, + int (*cb)(void *ctx, struct http_cert *cert), + void *cb_ctx) +{ + ctx->cert_cb = cb; + ctx->cert_cb_ctx = cb_ctx; +} + + +const char * http_get_err(struct http_ctx *ctx) +{ + return ctx->last_err; +} diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c index 3647c764e672..92a359039033 100644 --- a/src/utils/ip_addr.c +++ b/src/utils/ip_addr.c @@ -33,30 +33,6 @@ const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, } -int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b) -{ - if (a == NULL && b == NULL) - return 0; - if (a == NULL || b == NULL) - return 1; - - switch (a->af) { - case AF_INET: - if (a->u.v4.s_addr != b->u.v4.s_addr) - return 1; - break; -#ifdef CONFIG_IPV6 - case AF_INET6: - if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0) - return 1; - break; -#endif /* CONFIG_IPV6 */ - } - - return 0; -} - - int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) { #ifndef CONFIG_NATIVE_WINDOWS diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h index 79ac20cdbdc9..0670411ccde4 100644 --- a/src/utils/ip_addr.h +++ b/src/utils/ip_addr.h @@ -22,7 +22,6 @@ struct hostapd_ip_addr { const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, size_t buflen); -int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b); int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr); #endif /* IP_ADDR_H */ diff --git a/src/utils/list.h b/src/utils/list.h index 6881130957ff..ee2f4856950f 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -17,6 +17,8 @@ struct dl_list { 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; diff --git a/src/utils/os.h b/src/utils/os.h index ad208341fa94..77250d6371c9 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -23,6 +23,11 @@ struct os_time { 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 @@ -30,21 +35,84 @@ struct os_time { */ 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); +} -/* Helper macros for handling struct os_time */ -#define os_time_before(a, b) \ - ((a)->sec < (b)->sec || \ - ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) +static inline int os_reltime_initialized(struct os_reltime *t) +{ + return t->sec != 0 || t->usec != 0; +} -#define os_time_sub(a, b, res) do { \ - (res)->sec = (a)->sec - (b)->sec; \ - (res)->usec = (a)->usec - (b)->usec; \ - if ((res)->usec < 0) { \ - (res)->sec--; \ - (res)->usec += 1000000; \ - } \ -} while (0) /** * os_mktime - Convert broken-down time into seconds since 1970-01-01 @@ -171,6 +239,13 @@ int os_unsetenv(const char *name); */ 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_zalloc - Allocate and zero memory * @size: Number of bytes to allocate @@ -360,15 +435,6 @@ int os_strcmp(const char *s1, const char *s2); */ int os_strncmp(const char *s1, const char *s2, size_t n); -/** - * os_strncpy - Copy a string - * @dest: Destination - * @src: Source - * @n: Maximum number of characters to copy - * Returns: dest - */ -char * os_strncpy(char *dest, const char *src, size_t n); - /** * os_strstr - Locate a substring * @haystack: String (haystack) to search from @@ -465,9 +531,6 @@ char * os_strdup(const char *s); #ifndef os_strncmp #define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) #endif -#ifndef os_strncpy -#define os_strncpy(d, s, n) strncpy((d), (s), (n)) -#endif #ifndef os_strrchr #define os_strrchr(s, c) strrchr((s), (c)) #endif @@ -486,6 +549,12 @@ char * os_strdup(const char *s); #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) @@ -493,6 +562,21 @@ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) 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 @@ -506,6 +590,32 @@ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) */ 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 diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index e4b7fdb18ad3..77733ad916cd 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -17,9 +17,11 @@ */ #include "includes.h" +#include +#include #undef OS_REJECT_C_LIB_FUNCTIONS -#include "os.h" +#include "common.h" void os_sleep(os_time_t sec, os_time_t usec) { @@ -41,6 +43,17 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -85,7 +98,7 @@ int os_gmtime(os_time_t t, struct os_tm *tm) int os_daemonize(const char *pid_file) { if (daemon(0, 0)) { - perror("daemon"); + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); return -1; } @@ -156,8 +169,8 @@ char * os_rel2abs_path(const char *rel_path) } } - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); + 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) { @@ -452,6 +465,20 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz) } +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; +} + + char * os_strstr(const char *haystack, const char *needle) { size_t len = os_strlen(needle); @@ -481,3 +508,57 @@ int os_snprintf(char *str, size_t size, const char *format, ...) str[size - 1] = '\0'; return ret; } + + +int os_exec(const char *program, const char *arg, int wait_completion) +{ + pid_t pid; + int pid_status; + + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); + 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); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + 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/src/utils/os_none.c b/src/utils/os_none.c index cabf73bd87fc..83fe025167b6 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -26,6 +26,12 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *t) +{ + return -1; +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -212,6 +218,11 @@ size_t os_strlcpy(char *dest, const char *src, size_t size) } +int os_memcmp_const(const void *a, const void *b, size_t len) +{ + return 0; +} + char * os_strstr(const char *haystack, const char *needle) { return NULL; @@ -223,3 +234,9 @@ int os_snprintf(char *str, size_t size, const char *format, ...) return 0; } #endif /* OS_NO_C_LIB_DEFINES */ + + +int os_exec(const char *program, const char *arg, int wait_completion) +{ + return -1; +} diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 23a93becea43..e0c1125d5305 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -9,23 +9,24 @@ #include "includes.h" #include +#include #ifdef ANDROID -#include -#include +#include +#include #include #endif /* ANDROID */ #include "os.h" +#include "common.h" #ifdef WPA_TRACE -#include "common.h" #include "wpa_debug.h" #include "trace.h" #include "list.h" -static struct dl_list alloc_list; +static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list); #define ALLOC_MAGIC 0xa84ef1b2 #define FREED_MAGIC 0x67fd487a @@ -60,6 +61,43 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *t) +{ +#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; + } + } +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -213,6 +251,9 @@ char * os_rel2abs_path(const char *rel_path) 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); @@ -257,11 +298,15 @@ int os_program_init(void) * 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(sizeof(groups)/sizeof(groups[0]), groups); + setgroups(ARRAY_SIZE(groups), groups); prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); @@ -276,9 +321,6 @@ int os_program_init(void) capset(&header, &cap); #endif /* ANDROID */ -#ifdef WPA_TRACE - dl_list_init(&alloc_list); -#endif /* WPA_TRACE */ return 0; } @@ -363,6 +405,16 @@ char * os_readfile(const char *name, size_t *len) } +int os_file_exists(const char *fname) +{ + FILE *f = fopen(fname, "rb"); + if (f == NULL) + return 0; + fclose(f); + return 1; +} + + #ifndef WPA_TRACE void * os_zalloc(size_t size) { @@ -396,11 +448,121 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz) } +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; +} + +#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; @@ -486,3 +648,57 @@ char * os_strdup(const char *s) } #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/src/utils/os_win32.c b/src/utils/os_win32.c index 163cebefce26..296ea13f153b 100644 --- a/src/utils/os_win32.c +++ b/src/utils/os_win32.c @@ -12,6 +12,7 @@ #include #include "os.h" +#include "common.h" void os_sleep(os_time_t sec, os_time_t usec) { @@ -47,6 +48,17 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *t) +{ + /* consider using performance counters or so instead */ + struct os_time now; + int res = os_get_time(&now); + t->sec = now.sec; + t->usec = now.usec; + return res; +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -233,3 +245,23 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz) 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; +} + + +int os_exec(const char *program, const char *arg, int wait_completion) +{ + return -1; +} diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c index 08510d015d9d..6f5ea9396213 100644 --- a/src/utils/pcsc_funcs.c +++ b/src/utils/pcsc_funcs.c @@ -281,77 +281,82 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", pos, end - pos); - while (pos + 1 < end) { + while (end - pos >= 2) { + unsigned char type, len; + + type = pos[0]; + len = pos[1]; wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", - pos[0], pos[1]); - if (pos + 2 + pos[1] > end) + type, len); + pos += 2; + + if (len > (unsigned int) (end - pos)) break; - switch (pos[0]) { + switch (type) { case USIM_TLV_FILE_DESC: wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_FILE_ID: wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_DF_NAME: wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_PROPR_INFO: wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " - "information TLV", pos + 2, pos[1]); + "information TLV", pos, len); break; case USIM_TLV_LIFE_CYCLE_STATUS: wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " - "Integer TLV", pos + 2, pos[1]); + "Integer TLV", pos, len); break; case USIM_TLV_FILE_SIZE: wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", - pos + 2, pos[1]); - if ((pos[1] == 1 || pos[1] == 2) && file_len) { - if (pos[1] == 1) - *file_len = (int) pos[2]; + pos, len); + if ((len == 1 || len == 2) && file_len) { + if (len == 1) + *file_len = (int) pos[0]; else - *file_len = ((int) pos[2] << 8) | - (int) pos[3]; + *file_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", *file_len); } break; case USIM_TLV_TOTAL_FILE_SIZE: wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_PIN_STATUS_TEMPLATE: wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " - "DO TLV", pos + 2, pos[1]); - if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && - pos[3] >= 1 && ps_do) { + "DO TLV", pos, len); + if (len >= 2 && pos[0] == USIM_PS_DO_TAG && + pos[1] >= 1 && ps_do) { wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", - pos[4]); - *ps_do = (int) pos[4]; + pos[2]); + *ps_do = (int) pos[2]; } break; case USIM_TLV_SHORT_FILE_ID: wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " - "Identifier (SFI) TLV", pos + 2, pos[1]); + "Identifier (SFI) TLV", pos, len); break; case USIM_TLV_SECURITY_ATTR_8B: case USIM_TLV_SECURITY_ATTR_8C: case USIM_TLV_SECURITY_ATTR_AB: wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " - "TLV", pos + 2, pos[1]); + "TLV", pos, len); break; default: wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", - pos, 2 + pos[1]); + pos, len); break; } - pos += 2 + pos[1]; + pos += len; if (pos == end) return 0; @@ -397,10 +402,12 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, unsigned char rid[5]; unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ } *efdir; - unsigned char buf[127]; + unsigned char buf[127], *aid_pos; size_t blen; + unsigned int aid_len = 0; efdir = (struct efdir *) buf; + aid_pos = &buf[4]; blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); @@ -449,14 +456,15 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, continue; } - if (efdir->aid_len < 1 || efdir->aid_len > 16) { - wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", - efdir->aid_len); + aid_len = efdir->aid_len; + if (aid_len < 1 || aid_len > 16) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u", + aid_len); continue; } wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", - efdir->rid, efdir->aid_len); + aid_pos, aid_len); if (efdir->appl_code[0] == 0x10 && efdir->appl_code[1] == 0x02) { @@ -472,30 +480,28 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, return -1; } - if (efdir->aid_len > maxlen) { + if (aid_len > maxlen) { wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); return -1; } - os_memcpy(aid, efdir->rid, efdir->aid_len); + os_memcpy(aid, aid_pos, aid_len); - return efdir->aid_len; + return aid_len; } /** * scard_init - Initialize SIM/USIM connection using PC/SC - * @sim_type: Allowed SIM types (SIM, USIM, or both) * @reader: Reader name prefix to search for * Returns: Pointer to private data structure, or %NULL on failure * * This function is used to initialize SIM/USIM connection. PC/SC is used to - * open connection to the SIM/USIM card and the card is verified to support the - * selected sim_type. In addition, local flag is set if a PIN is needed to - * access some of the card functions. Once the connection is not needed - * anymore, scard_deinit() can be used to close it. + * open connection to the SIM/USIM card. In addition, local flag is set if a + * PIN is needed to access some of the card functions. Once the connection is + * not needed anymore, scard_deinit() can be used to close it. */ -struct scard_data * scard_init(scard_sim_type sim_type, const char *reader) +struct scard_data * scard_init(const char *reader) { long ret; unsigned long len, pos; @@ -612,20 +618,14 @@ struct scard_data * scard_init(scard_sim_type sim_type, const char *reader) blen = sizeof(buf); - scard->sim_type = SCARD_GSM_SIM; - if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) { - wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); - if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, - SCARD_USIM, NULL, 0)) { - wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported"); - if (sim_type == SCARD_USIM_ONLY) - goto failed; - wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM"); - scard->sim_type = SCARD_GSM_SIM; - } else { - wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); - scard->sim_type = SCARD_USIM; - } + wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support"); + if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen, + SCARD_USIM, NULL, 0)) { + wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM"); + scard->sim_type = SCARD_GSM_SIM; + } else { + wpa_printf(MSG_DEBUG, "SCARD: USIM is supported"); + scard->sim_type = SCARD_USIM; } if (scard->sim_type == SCARD_GSM_SIM) { @@ -1104,7 +1104,7 @@ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) } if (scard->sim_type == SCARD_GSM_SIM) { - blen = (buf[2] << 8) | buf[3]; + blen = WPA_GET_BE16(&buf[2]); } else { int file_size; if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) @@ -1178,7 +1178,7 @@ int scard_get_mnc_len(struct scard_data *scard) } if (scard->sim_type == SCARD_GSM_SIM) { - file_size = (buf[2] << 8) | buf[3]; + file_size = WPA_GET_BE16(&buf[2]); } else { if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) return -3; @@ -1245,6 +1245,7 @@ int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, cmd[4] = 17; cmd[5] = 16; os_memcpy(cmd + 6, _rand, 16); + get_resp[0] = USIM_CLA; } len = sizeof(resp); ret = scard_transmit(scard, cmd, cmdlen, resp, &len); @@ -1413,6 +1414,12 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, pos += IK_LEN; wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); + if (end > pos) { + wpa_hexdump(MSG_DEBUG, + "SCARD: Ignore extra data in end", + pos, end - pos); + } + return 0; } diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h index b4ebc99835b6..eacd2a2d70f1 100644 --- a/src/utils/pcsc_funcs.h +++ b/src/utils/pcsc_funcs.h @@ -9,15 +9,8 @@ #ifndef PCSC_FUNCS_H #define PCSC_FUNCS_H -typedef enum { - SCARD_GSM_SIM_ONLY, - SCARD_USIM_ONLY, - SCARD_TRY_BOTH -} scard_sim_type; - - #ifdef PCSC_FUNCS -struct scard_data * scard_init(scard_sim_type sim_type, const char *reader); +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); @@ -34,7 +27,7 @@ int scard_supports_umts(struct scard_data *scard); #else /* PCSC_FUNCS */ -#define scard_init(s, r) NULL +#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 diff --git a/src/utils/platform.h b/src/utils/platform.h new file mode 100644 index 000000000000..46cfe785e180 --- /dev/null +++ b/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/src/utils/radiotap.c b/src/utils/radiotap.c index 804473fa4bfb..f8f815a86be9 100644 --- a/src/utils/radiotap.c +++ b/src/utils/radiotap.c @@ -2,6 +2,7 @@ * Radiotap parser * * Copyright 2007 Andy Green + * Copyright 2009 Johannes Berg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,34 +11,44 @@ * Alternatively, this software may be distributed under the terms of BSD * license. * - * See README and COPYING for more details. - * - * - * Modified for userspace by Johannes Berg - * I only modified some things on top to ease syncing should bugs be found. + * See COPYING for more details. */ - -#include "includes.h" - -#include "common.h" #include "radiotap_iter.h" - -#define le16_to_cpu le_to_host16 -#define le32_to_cpu le_to_host32 -#define __le32 uint32_t -#define ulong unsigned long -#define unlikely(cond) (cond) -#define get_unaligned(p) \ -({ \ - struct packed_dummy_struct { \ - typeof(*(p)) __val; \ - } __attribute__((packed)) *__ptr = (void *) (p); \ - \ - __ptr->__val; \ -}) +#include "platform.h" /* function prototypes and related defs are in radiotap_iter.h */ +static const struct radiotap_align_size rtap_namespace_sizes[] = { + [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, + [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, + [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, + [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, + [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, + [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, + /* + * add more here as they are defined in radiotap.h + */ +}; + +static const struct ieee80211_radiotap_namespace radiotap_ns = { + .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]), + .align_size = rtap_namespace_sizes, +}; + /** * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization * @iterator: radiotap_iterator to initialize @@ -73,38 +84,53 @@ * get_unaligned((type *)iterator.this_arg) to dereference * iterator.this_arg for type "type" safely on all arches. * - * Example code: - * See Documentation/networking/radiotap-headers.txt + * Example code: parse.c */ int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length) + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) { + /* must at least have the radiotap header */ + if (max_length < (int)sizeof(struct ieee80211_radiotap_header)) + return -EINVAL; + /* Linux only supports version 0 radiotap format */ if (radiotap_header->it_version) return -EINVAL; /* sanity check for allowed length and radiotap length field */ - if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) + if (max_length < get_unaligned_le16(&radiotap_header->it_len)) return -EINVAL; - iterator->rtheader = radiotap_header; - iterator->max_length = le16_to_cpu(get_unaligned( - &radiotap_header->it_len)); - iterator->arg_index = 0; - iterator->bitmap_shifter = le32_to_cpu(get_unaligned( - &radiotap_header->it_present)); - iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); - iterator->this_arg = NULL; + iterator->_rtheader = radiotap_header; + iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); + iterator->_arg_index = 0; + iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); + iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); + iterator->_next_ns_data = NULL; + iterator->_reset_on_ext = 0; + iterator->_next_bitmap = &radiotap_header->it_present; + iterator->_next_bitmap++; + iterator->_vns = vns; + iterator->current_namespace = &radiotap_ns; + iterator->is_radiotap_ns = 1; +#ifdef RADIOTAP_SUPPORT_OVERRIDES + iterator->n_overrides = 0; + iterator->overrides = NULL; +#endif /* find payload start allowing for extended bitmap(s) */ - if (unlikely(iterator->bitmap_shifter & (1<arg)) & - (1<arg += sizeof(u32); + if (iterator->_bitmap_shifter & (1<_arg - + (unsigned long)iterator->_rtheader + sizeof(uint32_t) > + (unsigned long)iterator->_max_length) + return -EINVAL; + while (get_unaligned_le32(iterator->_arg) & + (1 << IEEE80211_RADIOTAP_EXT)) { + iterator->_arg += sizeof(uint32_t); /* * check for insanity where the present bitmaps @@ -112,12 +138,14 @@ int ieee80211_radiotap_iterator_init( * stated radiotap header length */ - if (((ulong)iterator->arg - (ulong)iterator->rtheader) - > (ulong)iterator->max_length) + if ((unsigned long)iterator->_arg - + (unsigned long)iterator->_rtheader + + sizeof(uint32_t) > + (unsigned long)iterator->_max_length) return -EINVAL; } - iterator->arg += sizeof(u32); + iterator->_arg += sizeof(uint32_t); /* * no need to check again for blowing past stated radiotap @@ -126,11 +154,59 @@ int ieee80211_radiotap_iterator_init( */ } + iterator->this_arg = iterator->_arg; + iterator->this_arg_index = 0; + iterator->this_arg_size = 0; + /* we are all initialized happily */ return 0; } +static void find_ns(struct ieee80211_radiotap_iterator *iterator, + uint32_t oui, uint8_t subns) +{ + int i; + + iterator->current_namespace = NULL; + + if (!iterator->_vns) + return; + + for (i = 0; i < iterator->_vns->n_ns; i++) { + if (iterator->_vns->ns[i].oui != oui) + continue; + if (iterator->_vns->ns[i].subns != subns) + continue; + + iterator->current_namespace = &iterator->_vns->ns[i]; + break; + } +} + +#ifdef RADIOTAP_SUPPORT_OVERRIDES +static int find_override(struct ieee80211_radiotap_iterator *iterator, + int *align, int *size) +{ + int i; + + if (!iterator->overrides) + return 0; + + for (i = 0; i < iterator->n_overrides; i++) { + if (iterator->_arg_index == iterator->overrides[i].field) { + *align = iterator->overrides[i].align; + *size = iterator->overrides[i].size; + if (!*align) /* erroneous override */ + return 0; + return 1; + } + } + + return 0; +} +#endif + /** * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg @@ -156,99 +232,106 @@ int ieee80211_radiotap_iterator_init( */ int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator) + struct ieee80211_radiotap_iterator *iterator) { - - /* - * small length lookup table for all radiotap types we heard of - * starting from b0 in the bitmap, so we can walk the payload - * area of the radiotap header - * - * There is a requirement to pad args, so that args - * of a given length must begin at a boundary of that length - * -- but note that compound args are allowed (eg, 2 x u16 - * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not - * a reliable indicator of alignment requirement. - * - * upper nybble: content alignment for arg - * lower nybble: content length for arg - */ - - static const u8 rt_sizes[] = { - [IEEE80211_RADIOTAP_TSFT] = 0x88, - [IEEE80211_RADIOTAP_FLAGS] = 0x11, - [IEEE80211_RADIOTAP_RATE] = 0x11, - [IEEE80211_RADIOTAP_CHANNEL] = 0x24, - [IEEE80211_RADIOTAP_FHSS] = 0x22, - [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, - [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, - [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, - [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, - [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, - [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, - [IEEE80211_RADIOTAP_ANTENNA] = 0x11, - [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, - [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, - [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, - [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, - [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, - [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, - /* - * add more here as they are defined in - * include/net/ieee80211_radiotap.h - */ - }; - - /* - * for every radiotap entry we can at - * least skip (by knowing the length)... - */ - - while (iterator->arg_index < (int) sizeof(rt_sizes)) { + while (1) { int hit = 0; - int pad; + int pad, align, size, subns; + uint32_t oui; + + /* if no more EXT bits, that's it */ + if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && + !(iterator->_bitmap_shifter & 1)) + return -ENOENT; - if (!(iterator->bitmap_shifter & 1)) + if (!(iterator->_bitmap_shifter & 1)) goto next_entry; /* arg not present */ + /* get alignment/size of data */ + switch (iterator->_arg_index % 32) { + case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: + case IEEE80211_RADIOTAP_EXT: + align = 1; + size = 0; + break; + case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: + align = 2; + size = 6; + break; + default: +#ifdef RADIOTAP_SUPPORT_OVERRIDES + if (find_override(iterator, &align, &size)) { + /* all set */ + } else +#endif + if (!iterator->current_namespace || + iterator->_arg_index >= iterator->current_namespace->n_bits) { + if (iterator->current_namespace == &radiotap_ns) + return -ENOENT; + align = 0; + } else { + align = iterator->current_namespace->align_size[iterator->_arg_index].align; + size = iterator->current_namespace->align_size[iterator->_arg_index].size; + } + if (!align) { + /* skip all subsequent data */ + iterator->_arg = iterator->_next_ns_data; + /* give up on this namespace */ + iterator->current_namespace = NULL; + goto next_entry; + } + break; + } + /* * arg is present, account for alignment padding - * 8-bit args can be at any alignment - * 16-bit args must start on 16-bit boundary - * 32-bit args must start on 32-bit boundary - * 64-bit args must start on 64-bit boundary - * - * note that total arg size can differ from alignment of - * elements inside arg, so we use upper nybble of length - * table to base alignment on * - * also note: these alignments are ** relative to the - * start of the radiotap header **. There is no guarantee + * Note that these alignments are relative to the start + * of the radiotap header. There is no guarantee * that the radiotap header itself is aligned on any * kind of boundary. * - * the above is why get_unaligned() is used to dereference - * multibyte elements from the radiotap area + * The above is why get_unaligned() is used to dereference + * multibyte elements from the radiotap area. */ - pad = (((ulong)iterator->arg) - - ((ulong)iterator->rtheader)) & - ((rt_sizes[iterator->arg_index] >> 4) - 1); + pad = ((unsigned long)iterator->_arg - + (unsigned long)iterator->_rtheader) & (align - 1); if (pad) - iterator->arg += - (rt_sizes[iterator->arg_index] >> 4) - pad; + iterator->_arg += align - pad; + + if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { + int vnslen; + + if ((unsigned long)iterator->_arg + size - + (unsigned long)iterator->_rtheader > + (unsigned long)iterator->_max_length) + return -EINVAL; + + oui = (*iterator->_arg << 16) | + (*(iterator->_arg + 1) << 8) | + *(iterator->_arg + 2); + subns = *(iterator->_arg + 3); + + find_ns(iterator, oui, subns); + + vnslen = get_unaligned_le16(iterator->_arg + 4); + iterator->_next_ns_data = iterator->_arg + size + vnslen; + if (!iterator->current_namespace) + size += vnslen; + } /* * this is what we will return to user, but we need to * move on first so next call has something fresh to test */ - iterator->this_arg_index = iterator->arg_index; - iterator->this_arg = iterator->arg; - hit = 1; + iterator->this_arg_index = iterator->_arg_index; + iterator->this_arg = iterator->_arg; + iterator->this_arg_size = size; /* internally move on the size of this arg */ - iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; + iterator->_arg += size; /* * check for insanity where we are given a bitmap that @@ -257,31 +340,57 @@ int ieee80211_radiotap_iterator_next( * max_length on the last arg, never exceeding it. */ - if (((ulong)iterator->arg - (ulong)iterator->rtheader) > - (ulong) iterator->max_length) + if ((unsigned long)iterator->_arg - + (unsigned long)iterator->_rtheader > + (unsigned long)iterator->_max_length) return -EINVAL; - next_entry: - iterator->arg_index++; - if (unlikely((iterator->arg_index & 31) == 0)) { - /* completed current u32 bitmap */ - if (iterator->bitmap_shifter & 1) { - /* b31 was set, there is more */ - /* move to next u32 bitmap */ - iterator->bitmap_shifter = le32_to_cpu( - get_unaligned(iterator->next_bitmap)); - iterator->next_bitmap++; - } else - /* no more bitmaps: end */ - iterator->arg_index = sizeof(rt_sizes); - } else /* just try the next bit */ - iterator->bitmap_shifter >>= 1; + /* these special ones are valid in each bitmap word */ + switch (iterator->_arg_index % 32) { + case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: + iterator->_reset_on_ext = 1; + + iterator->is_radiotap_ns = 0; + /* + * If parser didn't register this vendor + * namespace with us, allow it to show it + * as 'raw. Do do that, set argument index + * to vendor namespace. + */ + iterator->this_arg_index = + IEEE80211_RADIOTAP_VENDOR_NAMESPACE; + if (!iterator->current_namespace) + hit = 1; + goto next_entry; + case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: + iterator->_reset_on_ext = 1; + iterator->current_namespace = &radiotap_ns; + iterator->is_radiotap_ns = 1; + goto next_entry; + case IEEE80211_RADIOTAP_EXT: + /* + * bit 31 was set, there is more + * -- move to next u32 bitmap + */ + iterator->_bitmap_shifter = + get_unaligned_le32(iterator->_next_bitmap); + iterator->_next_bitmap++; + if (iterator->_reset_on_ext) + iterator->_arg_index = 0; + else + iterator->_arg_index++; + iterator->_reset_on_ext = 0; + break; + default: + /* we've got a hit! */ + hit = 1; + next_entry: + iterator->_bitmap_shifter >>= 1; + iterator->_arg_index++; + } /* if we found a valid arg earlier, return it now */ if (hit) return 0; } - - /* we don't know how to handle any more args, we're done */ - return -ENOENT; } diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h index 137288f9a1e0..0572e7c963da 100644 --- a/src/utils/radiotap.h +++ b/src/utils/radiotap.h @@ -1,6 +1,3 @@ -/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ -/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ - /*- * Copyright (c) 2003, 2004 David Young. All rights reserved. * @@ -178,6 +175,14 @@ struct ieee80211_radiotap_header { * * Number of unicast retries a transmitted frame used. * + * IEEE80211_RADIOTAP_MCS u8, u8, u8 unitless + * + * Contains a bitmap of known fields/flags, the flags, and + * the MCS index. + * + * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitlesss + * + * Contains the AMPDU information for the subframe. */ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TSFT = 0, @@ -198,6 +203,13 @@ enum ieee80211_radiotap_type { IEEE80211_RADIOTAP_TX_FLAGS = 15, IEEE80211_RADIOTAP_RTS_RETRIES = 16, IEEE80211_RADIOTAP_DATA_RETRIES = 17, + + IEEE80211_RADIOTAP_MCS = 19, + IEEE80211_RADIOTAP_AMPDU_STATUS = 20, + + /* valid in every it_present bitmap, even vendor namespaces */ + IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, + IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, IEEE80211_RADIOTAP_EXT = 31 }; @@ -230,8 +242,10 @@ enum ieee80211_radiotap_type { * 802.11 header and payload * (to 32-bit boundary) */ +#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* frame failed FCS check */ + /* For IEEE80211_RADIOTAP_RX_FLAGS */ -#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ +#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* bad PLCP */ /* For IEEE80211_RADIOTAP_TX_FLAGS */ #define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive @@ -240,4 +254,38 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ #define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ +/* For IEEE80211_RADIOTAP_AMPDU_STATUS */ +#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 +#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002 +#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004 +#define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008 +#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010 +#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020 + +/* For IEEE80211_RADIOTAP_MCS */ +#define IEEE80211_RADIOTAP_MCS_HAVE_BW 0x01 +#define IEEE80211_RADIOTAP_MCS_HAVE_MCS 0x02 +#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 +#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 +#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 +#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20 +#define IEEE80211_RADIOTAP_MCS_HAVE_NESS 0x40 +#define IEEE80211_RADIOTAP_MCS_NESS_BIT1 0x80 + + +#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 +#define IEEE80211_RADIOTAP_MCS_BW_20 0 +#define IEEE80211_RADIOTAP_MCS_BW_40 1 +#define IEEE80211_RADIOTAP_MCS_BW_20L 2 +#define IEEE80211_RADIOTAP_MCS_BW_20U 3 +#define IEEE80211_RADIOTAP_MCS_SGI 0x04 +#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 +#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 +#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60 +#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5 +#define IEEE80211_RADIOTAP_MCS_STBC_1 1 +#define IEEE80211_RADIOTAP_MCS_STBC_2 2 +#define IEEE80211_RADIOTAP_MCS_STBC_3 3 +#define IEEE80211_RADIOTAP_MCS_NESS_BIT0 0x80 + #endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h index 2e0e87296ef5..b768c85baace 100644 --- a/src/utils/radiotap_iter.h +++ b/src/utils/radiotap_iter.h @@ -1,56 +1,96 @@ -/* - * Radiotap parser - * - * Copyright 2007 Andy Green - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - #ifndef __RADIOTAP_ITER_H #define __RADIOTAP_ITER_H +#include #include "radiotap.h" /* Radiotap header iteration * implemented in radiotap.c */ + +struct radiotap_override { + uint8_t field; + uint8_t align:4, size:4; +}; + +struct radiotap_align_size { + uint8_t align:4, size:4; +}; + +struct ieee80211_radiotap_namespace { + const struct radiotap_align_size *align_size; + int n_bits; + uint32_t oui; + uint8_t subns; +}; + +struct ieee80211_radiotap_vendor_namespaces { + const struct ieee80211_radiotap_namespace *ns; + int n_ns; +}; + /** * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args - * @rtheader: pointer to the radiotap header we are walking through - * @max_length: length of radiotap header in cpu byte ordering - * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg - * @this_arg: pointer to current radiotap arg - * @arg_index: internal next argument index - * @arg: internal next argument pointer - * @next_bitmap: internal pointer to next present u32 - * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + * @this_arg_index: index of current arg, valid after each successful call + * to ieee80211_radiotap_iterator_next() + * @this_arg: pointer to current radiotap arg; it is valid after each + * call to ieee80211_radiotap_iterator_next() but also after + * ieee80211_radiotap_iterator_init() where it will point to + * the beginning of the actual data portion + * @this_arg_size: length of the current arg, for convenience + * @current_namespace: pointer to the current namespace definition + * (or internally %NULL if the current namespace is unknown) + * @is_radiotap_ns: indicates whether the current namespace is the default + * radiotap namespace or not + * + * @overrides: override standard radiotap fields + * @n_overrides: number of overrides + * + * @_rtheader: pointer to the radiotap header we are walking through + * @_max_length: length of radiotap header in cpu byte ordering + * @_arg_index: next argument index + * @_arg: next argument pointer + * @_next_bitmap: internal pointer to next present u32 + * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + * @_vns: vendor namespace definitions + * @_next_ns_data: beginning of the next namespace's data + * @_reset_on_ext: internal; reset the arg index to 0 when going to the + * next bitmap word + * + * Describes the radiotap parser state. Fields prefixed with an underscore + * must not be used by users of the parser, only by the parser internally. */ struct ieee80211_radiotap_iterator { - struct ieee80211_radiotap_header *rtheader; - int max_length; - int this_arg_index; + struct ieee80211_radiotap_header *_rtheader; + const struct ieee80211_radiotap_vendor_namespaces *_vns; + const struct ieee80211_radiotap_namespace *current_namespace; + + unsigned char *_arg, *_next_ns_data; + uint32_t *_next_bitmap; + unsigned char *this_arg; +#ifdef RADIOTAP_SUPPORT_OVERRIDES + const struct radiotap_override *overrides; + int n_overrides; +#endif + int this_arg_index; + int this_arg_size; + + int is_radiotap_ns; - int arg_index; - unsigned char *arg; - uint32_t *next_bitmap; - uint32_t bitmap_shifter; + int _max_length; + int _arg_index; + uint32_t _bitmap_shifter; + int _reset_on_ext; }; extern int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length); + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); extern int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator); + struct ieee80211_radiotap_iterator *iterator); #endif /* __RADIOTAP_ITER_H */ diff --git a/src/utils/trace.c b/src/utils/trace.c index 6795d417d5d4..8484d277d24b 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -18,11 +18,9 @@ static struct dl_list active_references = #ifdef WPA_TRACE_BFD #include -#ifdef __linux__ -#include -#else /* __linux__ */ -#include -#endif /* __linux__ */ + +#define DMGL_PARAMS (1 << 0) +#define DMGL_ANSI (1 << 1) static char *prg_fname = NULL; static bfd *cached_abfd = NULL; @@ -35,7 +33,7 @@ static void get_prg_fname(void) os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); len = readlink(exe, fname, sizeof(fname) - 1); if (len < 0 || len >= (int) sizeof(fname)) { - perror("readlink"); + wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno)); return; } fname[len] = '\0'; @@ -162,7 +160,7 @@ static void wpa_trace_bfd_addr(void *pc) if (abfd == NULL) return; - data.pc = (bfd_vma) pc; + data.pc = (bfd_hostptr_t) pc; data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -187,6 +185,7 @@ static void wpa_trace_bfd_addr(void *pc) wpa_printf(MSG_INFO, " %s() %s:%u", name, filename, data.line); free(aname); + aname = NULL; data.found = bfd_find_inliner_info(abfd, &data.filename, &data.function, &data.line); @@ -202,7 +201,7 @@ static const char * wpa_trace_bfd_addr2func(void *pc) if (abfd == NULL) return NULL; - data.pc = (bfd_vma) pc; + data.pc = (bfd_hostptr_t) pc; data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -244,6 +243,53 @@ void wpa_trace_dump_funcname(const char *title, void *pc) wpa_trace_bfd_addr(pc); } + +size_t wpa_trace_calling_func(const char *buf[], size_t len) +{ + bfd *abfd; + void *btrace_res[WPA_TRACE_LEN]; + int i, btrace_num; + size_t pos = 0; + + if (len == 0) + return 0; + if (len > WPA_TRACE_LEN) + len = WPA_TRACE_LEN; + + wpa_trace_bfd_init(); + abfd = cached_abfd; + if (!abfd) + return 0; + + btrace_num = backtrace(btrace_res, len); + if (btrace_num < 1) + return 0; + + for (i = 0; i < btrace_num; i++) { + struct bfd_data data; + + data.pc = (bfd_hostptr_t) btrace_res[i]; + data.found = FALSE; + bfd_map_over_sections(abfd, find_addr_sect, &data); + + while (data.found) { + if (data.function && + (pos > 0 || + os_strcmp(data.function, __func__) != 0)) { + buf[pos++] = data.function; + if (pos == len) + return pos; + } + + data.found = bfd_find_inliner_info(abfd, &data.filename, + &data.function, + &data.line); + } + } + + return pos; +} + #else /* WPA_TRACE_BFD */ #define wpa_trace_bfd_init() do { } while (0) diff --git a/src/utils/trace.h b/src/utils/trace.h index 38f43fbfab96..43ed86c19978 100644 --- a/src/utils/trace.h +++ b/src/utils/trace.h @@ -40,6 +40,7 @@ void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *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 */ diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c new file mode 100644 index 000000000000..4b97dadd786c --- /dev/null +++ b/src/utils/utils_module_tests.c @@ -0,0 +1,423 @@ +/* + * utils module tests + * Copyright (c) 2014-2015, Jouni Malinen + * + * 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/bitfield.h" +#include "utils/ext_password.h" +#include "utils/trace.h" +#include "utils/base64.h" + + +struct printf_test_data { + u8 *data; + size_t len; + char *encoded; +}; + +static const struct printf_test_data printf_tests[] = { + { (u8 *) "abcde", 5, "abcde" }, + { (u8 *) "a\0b\nc\ed\re\tf\"\\", 13, "a\\0b\\nc\\ed\\re\\tf\\\"\\\\" }, + { (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" }, + { (u8 *) "\n\n\n", 3, "\n\12\x0a" }, + { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12, + "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" }, + { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12, + "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" }, + { (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6, + "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" }, + { NULL, 0, NULL } +}; + + +static int printf_encode_decode_tests(void) +{ + int i; + size_t binlen; + char buf[100]; + u8 bin[100]; + int errors = 0; + + wpa_printf(MSG_INFO, "printf encode/decode tests"); + + for (i = 0; printf_tests[i].data; i++) { + const struct printf_test_data *test = &printf_tests[i]; + printf_encode(buf, sizeof(buf), test->data, test->len); + wpa_printf(MSG_INFO, "%d: -> \"%s\"", i, buf); + + binlen = printf_decode(bin, sizeof(bin), buf); + if (binlen != test->len || + os_memcmp(bin, test->data, binlen) != 0) { + wpa_hexdump(MSG_ERROR, "Error in decoding#1", + bin, binlen); + errors++; + } + + binlen = printf_decode(bin, sizeof(bin), test->encoded); + if (binlen != test->len || + os_memcmp(bin, test->data, binlen) != 0) { + wpa_hexdump(MSG_ERROR, "Error in decoding#2", + bin, binlen); + errors++; + } + } + + buf[5] = 'A'; + printf_encode(buf, 5, (const u8 *) "abcde", 5); + if (buf[5] != 'A') { + wpa_printf(MSG_ERROR, "Error in bounds checking#1"); + errors++; + } + + for (i = 5; i < 10; i++) { + buf[i] = 'A'; + printf_encode(buf, i, (const u8 *) "\xdd\xdd\xdd\xdd\xdd", 5); + if (buf[i] != 'A') { + wpa_printf(MSG_ERROR, "Error in bounds checking#2(%d)", + i); + errors++; + } + } + + if (printf_decode(bin, 3, "abcde") != 2) + errors++; + + if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10) + errors++; + + if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a') + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int bitfield_tests(void) +{ + struct bitfield *bf; + int i; + int errors = 0; + + wpa_printf(MSG_INFO, "bitfield tests"); + + bf = bitfield_alloc(123); + if (bf == NULL) + return -1; + + for (i = 0; i < 123; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + if (i > 0 && bitfield_is_set(bf, i - 1)) + errors++; + bitfield_set(bf, i); + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 123; i < 200; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + if (i > 0 && bitfield_is_set(bf, i - 1)) + errors++; + bitfield_set(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + bitfield_set(bf, i); + if (!bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (bitfield_get_first_zero(bf) != i) + errors++; + bitfield_set(bf, i); + } + if (bitfield_get_first_zero(bf) != -1) + errors++; + for (i = 0; i < 123; i++) { + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_get_first_zero(bf) != i) + errors++; + bitfield_set(bf, i); + } + if (bitfield_get_first_zero(bf) != -1) + errors++; + + bitfield_free(bf); + + bf = bitfield_alloc(8); + if (bf == NULL) + return -1; + if (bitfield_get_first_zero(bf) != 0) + errors++; + for (i = 0; i < 8; i++) + bitfield_set(bf, i); + if (bitfield_get_first_zero(bf) != -1) + errors++; + bitfield_free(bf); + + if (errors) { + wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int int_array_tests(void) +{ + int test1[] = { 1, 2, 3, 4, 5, 6, 0 }; + int test2[] = { 1, -1, 0 }; + int test3[] = { 1, 1, 1, -1, 2, 3, 4, 1, 2, 0 }; + int test3_res[] = { -1, 1, 2, 3, 4, 0 }; + int errors = 0; + int len; + + wpa_printf(MSG_INFO, "int_array tests"); + + if (int_array_len(test1) != 6 || + int_array_len(test2) != 2) + errors++; + + int_array_sort_unique(test3); + len = int_array_len(test3_res); + if (int_array_len(test3) != len) + errors++; + else if (os_memcmp(test3, test3_res, len * sizeof(int)) != 0) + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d int_array test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int ext_password_tests(void) +{ + struct ext_password_data *data; + int ret = 0; + struct wpabuf *pw; + + wpa_printf(MSG_INFO, "ext_password tests"); + + data = ext_password_init("unknown", "foo"); + if (data != NULL) + return -1; + + data = ext_password_init("test", NULL); + if (data == NULL) + return -1; + pw = ext_password_get(data, "foo"); + if (pw != NULL) + ret = -1; + ext_password_free(pw); + + ext_password_deinit(data); + + pw = ext_password_get(NULL, "foo"); + if (pw != NULL) + ret = -1; + ext_password_free(pw); + + return ret; +} + + +static int trace_tests(void) +{ + wpa_printf(MSG_INFO, "trace tests"); + + wpa_trace_show("test backtrace"); + wpa_trace_dump_funcname("test funcname", trace_tests); + + return 0; +} + + +static int base64_tests(void) +{ + int errors = 0; + unsigned char *res; + size_t res_len; + + wpa_printf(MSG_INFO, "base64 tests"); + + res = base64_encode((const unsigned char *) "", ~0, &res_len); + if (res) { + errors++; + os_free(res); + } + + res = base64_encode((const unsigned char *) "=", 1, &res_len); + if (!res || res_len != 5 || res[0] != 'P' || res[1] != 'Q' || + res[2] != '=' || res[3] != '=' || res[4] != '\n') + errors++; + os_free(res); + + res = base64_encode((const unsigned char *) "=", 1, NULL); + if (!res || res[0] != 'P' || res[1] != 'Q' || + res[2] != '=' || res[3] != '=' || res[4] != '\n') + errors++; + os_free(res); + + res = base64_decode((const unsigned char *) "", 0, &res_len); + if (res) { + errors++; + os_free(res); + } + + res = base64_decode((const unsigned char *) "a", 1, &res_len); + if (res) { + errors++; + os_free(res); + } + + res = base64_decode((const unsigned char *) "====", 4, &res_len); + if (res) { + errors++; + os_free(res); + } + + res = base64_decode((const unsigned char *) "PQ==", 4, &res_len); + if (!res || res_len != 1 || res[0] != '=') + errors++; + os_free(res); + + res = base64_decode((const unsigned char *) "P.Q-=!=*", 8, &res_len); + if (!res || res_len != 1 || res[0] != '=') + errors++; + os_free(res); + + if (errors) { + wpa_printf(MSG_ERROR, "%d base64 test(s) failed", errors); + return -1; + } + + return 0; +} + + +static int common_tests(void) +{ + char buf[3]; + u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 }; + u8 bin[3]; + int errors = 0; + struct wpa_freq_range_list ranges; + + wpa_printf(MSG_INFO, "common tests"); + + if (hwaddr_mask_txt(buf, 3, addr, addr) != -1) + errors++; + + if (wpa_scnprintf(buf, 0, "hello") != 0 || + wpa_scnprintf(buf, 3, "hello") != 2) + errors++; + + if (wpa_snprintf_hex(buf, 0, addr, ETH_ALEN) != 0 || + wpa_snprintf_hex(buf, 3, addr, ETH_ALEN) != 2) + errors++; + + if (merge_byte_arrays(bin, 3, addr, ETH_ALEN, NULL, 0) != 3 || + merge_byte_arrays(bin, 3, NULL, 0, addr, ETH_ALEN) != 3) + errors++; + + if (dup_binstr(NULL, 0) != NULL) + errors++; + + if (freq_range_list_includes(NULL, 0) != 0) + errors++; + + os_memset(&ranges, 0, sizeof(ranges)); + if (freq_range_list_parse(&ranges, "") != 0 || + freq_range_list_includes(&ranges, 0) != 0 || + freq_range_list_str(&ranges) != NULL) + errors++; + + if (utf8_unescape(NULL, 0, buf, sizeof(buf)) != 0 || + utf8_unescape("a", 1, NULL, 0) != 0 || + utf8_unescape("a\\", 2, buf, sizeof(buf)) != 0 || + utf8_unescape("abcde", 5, buf, sizeof(buf)) != 0 || + utf8_unescape("abc", 3, buf, 3) != 3) + errors++; + + if (utf8_unescape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a') + errors++; + + if (utf8_unescape("\\b", 2, buf, sizeof(buf)) != 1 || buf[0] != 'b') + errors++; + + if (utf8_escape(NULL, 0, buf, sizeof(buf)) != 0 || + utf8_escape("a", 1, NULL, 0) != 0 || + utf8_escape("abcde", 5, buf, sizeof(buf)) != 0 || + utf8_escape("a\\bcde", 6, buf, sizeof(buf)) != 0 || + utf8_escape("ab\\cde", 6, buf, sizeof(buf)) != 0 || + utf8_escape("abc\\de", 6, buf, sizeof(buf)) != 0 || + utf8_escape("abc", 3, buf, 3) != 3) + errors++; + + if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a') + errors++; + + if (errors) { + wpa_printf(MSG_ERROR, "%d common test(s) failed", errors); + return -1; + } + + return 0; +} + + +int utils_module_tests(void) +{ + int ret = 0; + + wpa_printf(MSG_INFO, "utils module tests"); + + if (printf_encode_decode_tests() < 0 || + ext_password_tests() < 0 || + trace_tests() < 0 || + bitfield_tests() < 0 || + base64_tests() < 0 || + common_tests() < 0 || + int_array_tests() < 0) + ret = -1; + + return ret; +} diff --git a/src/utils/uuid.c b/src/utils/uuid.c index 2aa4bcb5fa19..0f224f976b80 100644 --- a/src/utils/uuid.c +++ b/src/utils/uuid.c @@ -55,7 +55,7 @@ int uuid_bin2str(const u8 *bin, char *str, size_t max_len) bin[4], bin[5], bin[6], bin[7], bin[8], bin[9], bin[10], bin[11], bin[12], bin[13], bin[14], bin[15]); - if (len < 0 || (size_t) len >= max_len) + if (os_snprintf_error(max_len, len)) return -1; return 0; } diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index 5511ef193b0c..0d1190518536 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / Debug prints - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -375,19 +375,19 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, #endif /* CONFIG_ANDROID_LOG */ } -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +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 u8 *buf, size_t len) +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 u8 *buf, +static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len, int show) { size_t i, llen; @@ -407,7 +407,7 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, /* can do ascii processing in userspace */ for (i = 0; i < len; i++) fprintf(wpa_debug_tracing_file, - " %02x", buf[i]); + " %02x", pos[i]); } fflush(wpa_debug_tracing_file); } @@ -495,13 +495,14 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, } -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +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 u8 *buf, +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); @@ -554,6 +555,8 @@ int wpa_debug_open_file(const char *path) #ifndef _WIN32 setvbuf(out_file, NULL, _IOLBF, 0); #endif /* _WIN32 */ +#else /* CONFIG_DEBUG_FILE */ + (void)path; #endif /* CONFIG_DEBUG_FILE */ return 0; } @@ -571,6 +574,14 @@ void wpa_debug_close_file(void) #endif /* CONFIG_DEBUG_FILE */ } + +void wpa_debug_setup_stdout(void) +{ +#ifndef _WIN32 + setvbuf(stdout, NULL, _IOLBF, 0); +#endif /* _WIN32 */ +} + #endif /* CONFIG_NO_STDOUT_DEBUG */ @@ -595,10 +606,14 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; - const int buflen = 2048; + 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 " @@ -612,7 +627,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) if (ifname) { int res = os_snprintf(prefix, sizeof(prefix), "%s: ", ifname); - if (res < 0 || res >= (int) sizeof(prefix)) + if (os_snprintf_error(sizeof(prefix), res)) prefix[0] = '\0'; } } @@ -620,7 +635,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) va_end(ap); wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, buf, len); + wpa_msg_cb(ctx, level, 0, buf, len); os_free(buf); } @@ -629,12 +644,16 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; - const int buflen = 2048; + 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 " @@ -644,9 +663,92 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, buf, len); + wpa_msg_cb(ctx, level, 0, buf, len); + os_free(buf); +} + + +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, 1, buf, len); + os_free(buf); +} + + +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, 1, buf, len); + os_free(buf); +} + + +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, 2, buf, len); os_free(buf); } + #endif /* CONFIG_NO_WPA_MSG */ @@ -664,9 +766,13 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, { va_list ap; char *buf; - const int buflen = 2048; + 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 " diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 339c749ce582..400bea9e599f 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / Debug prints - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,10 @@ #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. */ @@ -30,6 +34,7 @@ enum { #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) @@ -42,6 +47,7 @@ static inline int wpa_debug_reopen_file(void) 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 @@ -77,7 +83,7 @@ PRINTF_FORMAT(2, 3); * 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 u8 *buf, size_t len); +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) @@ -99,7 +105,7 @@ static inline void wpa_hexdump_buf(int level, const char *title, * 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 u8 *buf, size_t len); +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) @@ -121,7 +127,7 @@ static inline void wpa_hexdump_buf_key(int level, const char *title, * 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 u8 *buf, +void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len); /** @@ -138,7 +144,7 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, * 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 u8 *buf, +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len); /* @@ -155,6 +161,9 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, #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_register_cb(f) do { } while (0) #define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ @@ -189,8 +198,53 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); -typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, - size_t len); +/** + * 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); + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, + const char *txt, size_t len); /** * wpa_msg_register_cb - Register callback function for wpa_msg() messages diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index b257b365c756..7aafa0a5169b 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -205,6 +205,15 @@ void wpabuf_free(struct wpabuf *buf) } +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); diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h index dbce925ca1bb..c3ef1bae3667 100644 --- a/src/utils/wpabuf.h +++ b/src/utils/wpabuf.h @@ -32,6 +32,7 @@ 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); diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c new file mode 100644 index 000000000000..4916d29765f9 --- /dev/null +++ b/src/utils/xml-utils.c @@ -0,0 +1,471 @@ +/* + * Generic XML helper functions + * Copyright (c) 2012-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 "xml-utils.h" + + +static xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx, + xml_node_t *root, char *uri) +{ + char *end; + xml_node_t *node; + const char *name; + + end = strchr(uri, '/'); + if (end) + *end++ = '\0'; + + node = root; + xml_node_for_each_sibling(ctx, node) { + xml_node_for_each_check(ctx, node); + name = xml_node_get_localname(ctx, node); + if (strcasecmp(name, uri) == 0) + break; + } + + if (node == NULL) + return NULL; + + if (end) { + return get_node_uri_iter(ctx, xml_node_first_child(ctx, node), + end); + } + + return node; +} + + +xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root, + const char *uri) +{ + char *search; + xml_node_t *node; + + search = os_strdup(uri); + if (search == NULL) + return NULL; + + node = get_node_uri_iter(ctx, root, search); + + os_free(search); + return node; +} + + +static xml_node_t * get_node_iter(struct xml_node_ctx *ctx, + xml_node_t *root, const char *path) +{ + char *end; + xml_node_t *node; + const char *name; + + end = os_strchr(path, '/'); + if (end) + *end++ = '\0'; + + xml_node_for_each_child(ctx, node, root) { + xml_node_for_each_check(ctx, node); + name = xml_node_get_localname(ctx, node); + if (os_strcasecmp(name, path) == 0) + break; + } + + if (node == NULL) + return NULL; + if (end) + return get_node_iter(ctx, node, end); + return node; +} + + +xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root, + const char *path) +{ + char *search; + xml_node_t *node; + + search = os_strdup(path); + if (search == NULL) + return NULL; + + node = get_node_iter(ctx, root, search); + + os_free(search); + return node; +} + + +xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root, + const char *path) +{ + xml_node_t *node; + xml_node_t *match; + + xml_node_for_each_child(ctx, node, root) { + xml_node_for_each_check(ctx, node); + match = get_node(ctx, node, path); + if (match) + return match; + } + + return NULL; +} + + +xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name) +{ + xml_node_t *node; + char *buf, *buf2, *start; + size_t len; + + buf = os_readfile(name, &len); + if (buf == NULL) + return NULL; + buf2 = os_realloc(buf, len + 1); + if (buf2 == NULL) { + os_free(buf); + return NULL; + } + buf = buf2; + buf[len] = '\0'; + + start = os_strstr(buf, "') { + count--; + if (count == 0) { + pos++; + break; + } + } + pos++; + } + if (count == 0) { + /* Remove DOCTYPE to allow the file to be parsed */ + os_memset(start, ' ', pos - start); + } + } + + node = xml_node_from_buf(ctx, buf); + os_free(buf); + + return node; +} + + +int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node) +{ + FILE *f; + char *str; + + str = xml_node_to_str(ctx, node); + if (str == NULL) + return -1; + + f = fopen(fname, "w"); + if (!f) { + os_free(str); + return -1; + } + + fprintf(f, "%s\n", str); + os_free(str); + fclose(f); + + return 0; +} + + +static char * get_val(struct xml_node_ctx *ctx, xml_node_t *node) +{ + char *val, *pos; + + val = xml_node_get_text(ctx, node); + if (val == NULL) + return NULL; + pos = val; + while (*pos) { + if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n') + return val; + pos++; + } + + return NULL; +} + + +static char * add_path(const char *prev, const char *leaf) +{ + size_t len; + char *new_uri; + + if (prev == NULL) + return NULL; + + len = os_strlen(prev) + 1 + os_strlen(leaf) + 1; + new_uri = os_malloc(len); + if (new_uri) + os_snprintf(new_uri, len, "%s/%s", prev, leaf); + + return new_uri; +} + + +static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out, + xml_node_t *in, const char *uri) +{ + xml_node_t *node; + xml_node_t *tnds; + const char *name; + char *val; + char *new_uri; + + xml_node_for_each_child(ctx, node, in) { + xml_node_for_each_check(ctx, node); + name = xml_node_get_localname(ctx, node); + + tnds = xml_node_create(ctx, out, NULL, "Node"); + if (tnds == NULL) + return; + xml_node_create_text(ctx, tnds, NULL, "NodeName", name); + + if (uri) + xml_node_create_text(ctx, tnds, NULL, "Path", uri); + + val = get_val(ctx, node); + if (val) { + xml_node_create_text(ctx, tnds, NULL, "Value", val); + xml_node_get_text_free(ctx, val); + } + + new_uri = add_path(uri, name); + node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri); + os_free(new_uri); + } +} + + +static int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent, + const char *urn) +{ + xml_node_t *node; + + node = xml_node_create(ctx, parent, NULL, "RTProperties"); + if (node == NULL) + return -1; + node = xml_node_create(ctx, node, NULL, "Type"); + if (node == NULL) + return -1; + xml_node_create_text(ctx, node, NULL, "DDFName", urn); + return 0; +} + + +xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo, + int use_path, const char *urn, const char *ns_uri) +{ + xml_node_t *root; + xml_node_t *node; + const char *name; + + root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree"); + if (root == NULL) + return NULL; + + xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2"); + + name = xml_node_get_localname(ctx, mo); + + node = xml_node_create(ctx, root, NULL, "Node"); + if (node == NULL) + goto fail; + xml_node_create_text(ctx, node, NULL, "NodeName", name); + if (urn) + add_ddfname(ctx, node, urn); + + node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL); + + return root; + +fail: + xml_node_free(ctx, root); + return NULL; +} + + +static xml_node_t * get_first_child_node(struct xml_node_ctx *ctx, + xml_node_t *node, + const char *name) +{ + const char *lname; + xml_node_t *child; + + xml_node_for_each_child(ctx, child, node) { + xml_node_for_each_check(ctx, child); + lname = xml_node_get_localname(ctx, child); + if (os_strcasecmp(lname, name) == 0) + return child; + } + + return NULL; +} + + +static char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node, + const char *node_name) +{ + node = get_first_child_node(ctx, node, node_name); + if (node == NULL) + return NULL; + return xml_node_get_text(ctx, node); +} + + +static xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root, + xml_node_t *node, const char *uri) +{ + char *nodename, *value, *path; + xml_node_t *parent; + + nodename = get_node_text(ctx, node, "NodeName"); + if (nodename == NULL) + return NULL; + value = get_node_text(ctx, node, "Value"); + + if (root == NULL) { + root = xml_node_create_root(ctx, NULL, NULL, NULL, + nodename); + if (root && value) + xml_node_set_text(ctx, root, value); + } else { + if (uri == NULL) { + xml_node_get_text_free(ctx, nodename); + xml_node_get_text_free(ctx, value); + return NULL; + } + path = get_node_text(ctx, node, "Path"); + if (path) + uri = path; + parent = get_node_uri(ctx, root, uri); + xml_node_get_text_free(ctx, path); + if (parent == NULL) { + printf("Could not find URI '%s'\n", uri); + xml_node_get_text_free(ctx, nodename); + xml_node_get_text_free(ctx, value); + return NULL; + } + if (value) + xml_node_create_text(ctx, parent, NULL, nodename, + value); + else + xml_node_create(ctx, parent, NULL, nodename); + } + + xml_node_get_text_free(ctx, nodename); + xml_node_get_text_free(ctx, value); + + return root; +} + + +static xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root, + xml_node_t *node, const char *uri) +{ + xml_node_t *child; + const char *name; + char *nodename; + + xml_node_for_each_sibling(ctx, node) { + xml_node_for_each_check(ctx, node); + + nodename = get_node_text(ctx, node, "NodeName"); + if (nodename == NULL) + return NULL; + + name = xml_node_get_localname(ctx, node); + if (strcmp(name, "Node") == 0) { + if (root && !uri) { + printf("Invalid TNDS tree structure - " + "multiple top level nodes\n"); + xml_node_get_text_free(ctx, nodename); + return NULL; + } + root = add_mo_node(ctx, root, node, uri); + } + + child = get_first_child_node(ctx, node, "Node"); + if (child) { + if (uri == NULL) + tnds_to_mo_iter(ctx, root, child, nodename); + else { + char *new_uri; + new_uri = add_path(uri, nodename); + tnds_to_mo_iter(ctx, root, child, new_uri); + os_free(new_uri); + } + } + xml_node_get_text_free(ctx, nodename); + } + + return root; +} + + +xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds) +{ + const char *name; + xml_node_t *node; + + name = xml_node_get_localname(ctx, tnds); + if (name == NULL || os_strcmp(name, "MgmtTree") != 0) + return NULL; + + node = get_first_child_node(ctx, tnds, "Node"); + if (!node) + return NULL; + return tnds_to_mo_iter(ctx, NULL, node, NULL); +} + + +xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node) +{ + xml_node_t *envelope, *body; + xml_namespace_t *ns; + + envelope = xml_node_create_root( + ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns, + "Envelope"); + if (envelope == NULL) + return NULL; + body = xml_node_create(ctx, envelope, ns, "Body"); + xml_node_add_child(ctx, body, node); + return envelope; +} + + +xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap) +{ + xml_node_t *body, *child; + + body = get_node_uri(ctx, soap, "Envelope/Body"); + if (body == NULL) + return NULL; + xml_node_for_each_child(ctx, child, body) { + xml_node_for_each_check(ctx, child); + return child; + } + return NULL; +} diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h new file mode 100644 index 000000000000..fb6208cdac32 --- /dev/null +++ b/src/utils/xml-utils.h @@ -0,0 +1,97 @@ +/* + * Generic XML helper functions + * Copyright (c) 2012-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef XML_UTILS_H +#define XML_UTILS_H + +struct xml_node_ctx; +typedef struct xml_node xml_node_t; +typedef struct xml_namespace_foo xml_namespace_t; + +/* XML library wrappers */ + +int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node, + const char *xml_schema_fname, char **ret_err); +int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node, + const char *dtd_fname, char **ret_err); +void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node); +xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node); +xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf); +const char * xml_node_get_localname(struct xml_node_ctx *ctx, + xml_node_t *node); +char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node); +void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node); +void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent, + xml_node_t *child); +xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri, + const char *ns_prefix, + xml_namespace_t **ret_ns, const char *name); +xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent, + xml_namespace_t *ns, const char *name); +xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx, + xml_node_t *parent, xml_namespace_t *ns, + const char *name, const char *value); +xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx, + xml_node_t *parent, const char *ns_uri, + const char *name, const char *value); +void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node, + const char *value); +int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node, + xml_namespace_t *ns, const char *name, const char *value); +char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, + char *name); +char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node, + const char *ns_uri, char *name); +void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val); +xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx, + xml_node_t *parent); +xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx, + xml_node_t *node); +int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node); +char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node); +void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val); +char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node, + int *ret_len); +xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node); + +#define xml_node_for_each_child(ctx, child, parent) \ +for (child = xml_node_first_child(ctx, parent); \ + child; \ + child = xml_node_next_sibling(ctx, child)) + +#define xml_node_for_each_sibling(ctx, node) \ +for (; \ + node; \ + node = xml_node_next_sibling(ctx, node)) + +#define xml_node_for_each_check(ctx, child) \ +if (!xml_node_is_element(ctx, child)) \ + continue + + +struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx, + const void *env); +void xml_node_deinit_ctx(struct xml_node_ctx *ctx); + + +xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root, + const char *uri); +xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root, + const char *path); +xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root, + const char *path); +xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name); +int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node); +xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo, + int use_path, const char *urn, const char *ns_uri); +xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds); + +xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node); +xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap); + +#endif /* XML_UTILS_H */ diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c new file mode 100644 index 000000000000..c92839461dad --- /dev/null +++ b/src/utils/xml_libxml2.c @@ -0,0 +1,457 @@ +/* + * XML wrapper for libxml2 + * Copyright (c) 2012-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#define LIBXML_VALID_ENABLED +#include +#include + +#include "common.h" +#include "base64.h" +#include "xml-utils.h" + + +struct xml_node_ctx { + void *ctx; +}; + + +struct str_buf { + char *buf; + size_t len; +}; + +#define MAX_STR 1000 + +static void add_str(void *ctx_ptr, const char *fmt, ...) +{ + struct str_buf *str = ctx_ptr; + va_list ap; + char *n; + int len; + + n = os_realloc(str->buf, str->len + MAX_STR + 2); + if (n == NULL) + return; + str->buf = n; + + va_start(ap, fmt); + len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap); + va_end(ap); + if (len >= MAX_STR) + len = MAX_STR - 1; + str->len += len; + str->buf[str->len] = '\0'; +} + + +int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node, + const char *xml_schema_fname, char **ret_err) +{ + xmlDocPtr doc; + xmlNodePtr n; + xmlSchemaParserCtxtPtr pctx; + xmlSchemaValidCtxtPtr vctx; + xmlSchemaPtr schema; + int ret; + struct str_buf errors; + + if (ret_err) + *ret_err = NULL; + + doc = xmlNewDoc((xmlChar *) "1.0"); + if (doc == NULL) + return -1; + n = xmlDocCopyNode((xmlNodePtr) node, doc, 1); + if (n == NULL) { + xmlFreeDoc(doc); + return -1; + } + xmlDocSetRootElement(doc, n); + + os_memset(&errors, 0, sizeof(errors)); + + pctx = xmlSchemaNewParserCtxt(xml_schema_fname); + xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str, + (xmlSchemaValidityWarningFunc) add_str, + &errors); + schema = xmlSchemaParse(pctx); + xmlSchemaFreeParserCtxt(pctx); + + vctx = xmlSchemaNewValidCtxt(schema); + xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str, + (xmlSchemaValidityWarningFunc) add_str, + &errors); + + ret = xmlSchemaValidateDoc(vctx, doc); + xmlSchemaFreeValidCtxt(vctx); + xmlFreeDoc(doc); + xmlSchemaFree(schema); + + if (ret == 0) { + os_free(errors.buf); + return 0; + } else if (ret > 0) { + if (ret_err) + *ret_err = errors.buf; + else + os_free(errors.buf); + return -1; + } else { + if (ret_err) + *ret_err = errors.buf; + else + os_free(errors.buf); + return -1; + } +} + + +int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node, + const char *dtd_fname, char **ret_err) +{ + xmlDocPtr doc; + xmlNodePtr n; + xmlValidCtxt vctx; + xmlDtdPtr dtd; + int ret; + struct str_buf errors; + + if (ret_err) + *ret_err = NULL; + + doc = xmlNewDoc((xmlChar *) "1.0"); + if (doc == NULL) + return -1; + n = xmlDocCopyNode((xmlNodePtr) node, doc, 1); + if (n == NULL) { + xmlFreeDoc(doc); + return -1; + } + xmlDocSetRootElement(doc, n); + + os_memset(&errors, 0, sizeof(errors)); + + dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname); + if (dtd == NULL) { + xmlFreeDoc(doc); + return -1; + } + + os_memset(&vctx, 0, sizeof(vctx)); + vctx.userData = &errors; + vctx.error = add_str; + vctx.warning = add_str; + ret = xmlValidateDtd(&vctx, doc, dtd); + xmlFreeDoc(doc); + xmlFreeDtd(dtd); + + if (ret == 1) { + os_free(errors.buf); + return 0; + } else { + if (ret_err) + *ret_err = errors.buf; + else + os_free(errors.buf); + return -1; + } +} + + +void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node) +{ + xmlFreeNode((xmlNodePtr) node); +} + + +xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node) +{ + return (xml_node_t *) ((xmlNodePtr) node)->parent; +} + + +xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf) +{ + xmlDocPtr doc; + xmlNodePtr node; + + doc = xmlParseMemory(buf, strlen(buf)); + if (doc == NULL) + return NULL; + node = xmlDocGetRootElement(doc); + node = xmlCopyNode(node, 1); + xmlFreeDoc(doc); + + return (xml_node_t *) node; +} + + +const char * xml_node_get_localname(struct xml_node_ctx *ctx, + xml_node_t *node) +{ + return (const char *) ((xmlNodePtr) node)->name; +} + + +char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node) +{ + xmlChar *buf; + int bufsiz; + char *ret, *pos; + xmlNodePtr n = (xmlNodePtr) node; + xmlDocPtr doc; + + doc = xmlNewDoc((xmlChar *) "1.0"); + n = xmlDocCopyNode(n, doc, 1); + xmlDocSetRootElement(doc, n); + xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0); + xmlFreeDoc(doc); + pos = (char *) buf; + if (strncmp(pos, "'); + if (pos) + pos++; + while (pos && (*pos == '\r' || *pos == '\n')) + pos++; + } + if (pos) + ret = os_strdup(pos); + else + ret = NULL; + xmlFree(buf); + + if (ret) { + pos = ret; + if (pos[0]) { + while (pos[1]) + pos++; + } + while (pos >= ret && *pos == '\n') + *pos-- = '\0'; + } + + return ret; +} + + +void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node) +{ + xmlUnlinkNode((xmlNodePtr) node); +} + + +void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent, + xml_node_t *child) +{ + xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child); +} + + +xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri, + const char *ns_prefix, + xml_namespace_t **ret_ns, const char *name) +{ + xmlNodePtr node; + xmlNsPtr ns = NULL; + + node = xmlNewNode(NULL, (const xmlChar *) name); + if (node == NULL) + return NULL; + if (ns_uri) { + ns = xmlNewNs(node, (const xmlChar *) ns_uri, + (const xmlChar *) ns_prefix); + xmlSetNs(node, ns); + } + + if (ret_ns) + *ret_ns = (xml_namespace_t *) ns; + + return (xml_node_t *) node; +} + + +xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent, + xml_namespace_t *ns, const char *name) +{ + xmlNodePtr node; + node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns, + (const xmlChar *) name, NULL); + return (xml_node_t *) node; +} + + +xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx, + xml_node_t *parent, xml_namespace_t *ns, + const char *name, const char *value) +{ + xmlNodePtr node; + node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns, + (const xmlChar *) name, (const xmlChar *) value); + return (xml_node_t *) node; +} + + +xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx, + xml_node_t *parent, const char *ns_uri, + const char *name, const char *value) +{ + xmlNodePtr node; + xmlNsPtr ns; + + node = xmlNewTextChild((xmlNodePtr) parent, NULL, + (const xmlChar *) name, (const xmlChar *) value); + ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL); + xmlSetNs(node, ns); + return (xml_node_t *) node; +} + + +void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node, + const char *value) +{ + /* TODO: escape XML special chars in value */ + xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value); +} + + +int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node, + xml_namespace_t *ns, const char *name, const char *value) +{ + xmlAttrPtr attr; + + if (ns) { + attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns, + (const xmlChar *) name, + (const xmlChar *) value); + } else { + attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name, + (const xmlChar *) value); + } + + return attr ? 0 : -1; +} + + +char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node, + char *name) +{ + return (char *) xmlGetNoNsProp((xmlNodePtr) node, + (const xmlChar *) name); +} + + +char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node, + const char *ns_uri, char *name) +{ + return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name, + (const xmlChar *) ns_uri); +} + + +void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val) +{ + if (val) + xmlFree((xmlChar *) val); +} + + +xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx, + xml_node_t *parent) +{ + return (xml_node_t *) ((xmlNodePtr) parent)->children; +} + + +xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx, + xml_node_t *node) +{ + return (xml_node_t *) ((xmlNodePtr) node)->next; +} + + +int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node) +{ + return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE; +} + + +char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node) +{ + if (xmlChildElementCount((xmlNodePtr) node) > 0) + return NULL; + return (char *) xmlNodeGetContent((xmlNodePtr) node); +} + + +void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val) +{ + if (val) + xmlFree((xmlChar *) val); +} + + +char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node, + int *ret_len) +{ + char *txt; + unsigned char *ret; + size_t len; + + txt = xml_node_get_text(ctx, node); + if (txt == NULL) + return NULL; + + ret = base64_decode((unsigned char *) txt, strlen(txt), &len); + if (ret_len) + *ret_len = len; + xml_node_get_text_free(ctx, txt); + if (ret == NULL) + return NULL; + txt = os_malloc(len + 1); + if (txt == NULL) { + os_free(ret); + return NULL; + } + os_memcpy(txt, ret, len); + txt[len] = '\0'; + return txt; +} + + +xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node) +{ + if (node == NULL) + return NULL; + return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1); +} + + +struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx, + const void *env) +{ + struct xml_node_ctx *xctx; + + xctx = os_zalloc(sizeof(*xctx)); + if (xctx == NULL) + return NULL; + xctx->ctx = upper_ctx; + + LIBXML_TEST_VERSION + + return xctx; +} + + +void xml_node_deinit_ctx(struct xml_node_ctx *ctx) +{ + xmlSchemaCleanupTypes(); + xmlCleanupParser(); + xmlMemoryDump(); + os_free(ctx); +} diff --git a/src/wps/Makefile b/src/wps/Makefile index 9c41962fd7e1..adfd3dfd5b9b 100644 --- a/src/wps/Makefile +++ b/src/wps/Makefile @@ -2,7 +2,7 @@ all: @echo Nothing to be made. clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/wps/http_client.c b/src/wps/http_client.c index c6d6c7fdab2a..029001306cbe 100644 --- a/src/wps/http_client.c +++ b/src/wps/http_client.c @@ -92,7 +92,7 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) (unsigned long) wpabuf_len(c->req), (unsigned long) wpabuf_len(c->req) - c->req_pos); - res = send(c->sd, wpabuf_head(c->req) + c->req_pos, + res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, wpabuf_len(c->req) - c->req_pos, 0); if (res < 0) { wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", diff --git a/src/wps/http_server.c b/src/wps/http_server.c index 6ca32140ae83..ac088c429d60 100644 --- a/src/wps/http_server.c +++ b/src/wps/http_server.c @@ -232,6 +232,7 @@ struct http_server * http_server_init(struct in_addr *addr, int port, { struct sockaddr_in sin; struct http_server *srv; + int on = 1; srv = os_zalloc(sizeof(*srv)); if (srv == NULL) @@ -242,6 +243,15 @@ struct http_server * http_server_init(struct in_addr *addr, int port, 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) diff --git a/src/wps/httpread.c b/src/wps/httpread.c index ad4f4a1dcb8a..2f08f37275c0 100644 --- a/src/wps/httpread.c +++ b/src/wps/httpread.c @@ -67,8 +67,6 @@ struct httpread { int timeout_seconds; /* 0 or total duration timeout period */ /* dynamically used information follows */ - int sd_registered; /* nonzero if we need to unregister socket */ - int to_registered; /* nonzero if we need to unregister timeout */ int got_hdr; /* nonzero when header is finalized */ char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ @@ -129,19 +127,6 @@ static int word_eq(char *s1, char *s2) } -/* convert hex to binary - * Requires that c have been previously tested true with isxdigit(). - */ -static int hex_value(int c) -{ - if (isdigit(c)) - return c - '0'; - if (islower(c)) - return 10 + c - 'a'; - return 10 + c - 'A'; -} - - static void httpread_timeout_handler(void *eloop_data, void *user_ctx); /* httpread_destroy -- if h is non-NULL, clean up @@ -156,12 +141,8 @@ void httpread_destroy(struct httpread *h) if (!h) return; - if (h->to_registered) - eloop_cancel_timeout(httpread_timeout_handler, NULL, h); - h->to_registered = 0; - if (h->sd_registered) - eloop_unregister_sock(h->sd, EVENT_TYPE_READ); - h->sd_registered = 0; + 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 */ @@ -176,7 +157,6 @@ 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->to_registered = 0; /* is self-cancelling */ (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); } @@ -295,8 +275,7 @@ static int httpread_hdr_analyze(struct httpread *h) int c = *rawuri; if (c == '%' && isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { - *uri++ = (hex_value(rawuri[1]) << 4) | - hex_value(rawuri[2]); + *uri++ = hex2byte(rawuri + 1); rawuri += 3; } else { *uri++ = c; @@ -434,8 +413,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) */ if (httpread_debug >= 10) wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); - h->got_body = 1; - goto got_file; + h->got_body = 1; + goto got_file; } rbp = readbuf; @@ -638,7 +617,6 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * We do NOT support trailers except to skip them -- * this is supported (generally) by the http spec. */ - bbp = h->body + h->body_nbytes; for (;;) { int c; if (nread <= 0) @@ -703,15 +681,11 @@ got_file: * and just in case somehow we don't get destroyed right away, * unregister now. */ - if (h->sd_registered) - eloop_unregister_sock(h->sd, EVENT_TYPE_READ); - h->sd_registered = 0; + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); /* The application can destroy us whenever they feel like... * cancel timeout. */ - if (h->to_registered) - eloop_cancel_timeout(httpread_timeout_handler, NULL, h); - h->to_registered = 0; + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); } @@ -749,21 +723,17 @@ struct httpread * httpread_create( h->max_bytes = max_bytes; h->timeout_seconds = timeout_seconds; - if (timeout_seconds > 0) { - if (eloop_register_timeout(timeout_seconds, 0, - httpread_timeout_handler, - NULL, h)) { - /* No way to recover (from malloc failure) */ - goto fail; - } - h->to_registered = 1; + 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; } - h->sd_registered = 1; return h; fail: diff --git a/src/wps/ndef.c b/src/wps/ndef.c index a48a2d7cea1c..d45dfc8efee6 100644 --- a/src/wps/ndef.c +++ b/src/wps/ndef.c @@ -30,6 +30,7 @@ struct ndef_record { }; static char wifi_handover_type[] = "application/vnd.wfa.wsc"; +static char p2p_handover_type[] = "application/vnd.wfa.p2p"; static int ndef_parse_record(const u8 *data, u32 size, struct ndef_record *record) @@ -147,7 +148,8 @@ static struct wpabuf * ndef_build_record(u8 flags, void *type, static int wifi_filter(struct ndef_record *record) { - if (record->type_length != os_strlen(wifi_handover_type)) + if (record->type == NULL || + record->type_length != os_strlen(wifi_handover_type)) return 0; if (os_memcmp(record->type, wifi_handover_type, os_strlen(wifi_handover_type)) != 0) @@ -170,76 +172,27 @@ struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) } -struct wpabuf * ndef_build_wifi_hr(void) +static int p2p_filter(struct ndef_record *record) { - struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr; - struct wpabuf *carrier, *hc; - - rn = wpabuf_alloc(2); - if (rn == NULL) - return NULL; - wpabuf_put_be16(rn, os_random() & 0xffff); - - cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2, - NULL, 0, rn); - wpabuf_free(rn); - - if (cr == NULL) - return NULL; - - ac_payload = wpabuf_alloc(4); - if (ac_payload == NULL) { - wpabuf_free(cr); - return NULL; - } - wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */ - wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */ - wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */ - wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */ - - ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2, - NULL, 0, ac_payload); - wpabuf_free(ac_payload); - if (ac == NULL) { - wpabuf_free(cr); - return NULL; - } - - hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac)); - if (hr_payload == NULL) { - wpabuf_free(cr); - wpabuf_free(ac); - return NULL; - } + if (record->type == NULL || + record->type_length != os_strlen(p2p_handover_type)) + return 0; + if (os_memcmp(record->type, p2p_handover_type, + os_strlen(p2p_handover_type)) != 0) + return 0; + return 1; +} - wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */ - wpabuf_put_buf(hr_payload, cr); - wpabuf_put_buf(hr_payload, ac); - wpabuf_free(cr); - wpabuf_free(ac); - hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2, - NULL, 0, hr_payload); - wpabuf_free(hr_payload); - if (hr == NULL) - return NULL; +struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf) +{ + return ndef_parse_records(buf, p2p_filter); +} - carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type)); - if (carrier == NULL) { - wpabuf_free(hr); - return NULL; - } - wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */ - wpabuf_put_u8(carrier, os_strlen(wifi_handover_type)); - wpabuf_put_str(carrier, wifi_handover_type); - - hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2, - "0", 1, carrier); - wpabuf_free(carrier); - if (hc == NULL) { - wpabuf_free(hr); - return NULL; - } - return wpabuf_concat(hr, hc); +struct wpabuf * ndef_build_p2p(const struct wpabuf *buf) +{ + return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | + FLAG_TNF_RFC2046, p2p_handover_type, + os_strlen(p2p_handover_type), NULL, 0, buf); } diff --git a/src/wps/wps.c b/src/wps/wps.c index 2575705819cb..2c68be8c62ea 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -18,6 +18,7 @@ #ifdef CONFIG_WPS_TESTING int wps_version_number = 0x20; int wps_testing_dummy_cred = 0; +int wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ @@ -53,12 +54,22 @@ struct wps_data * wps_init(const struct wps_config *cfg) } 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; - os_free(data->dev_password); data->dev_password = os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); if (data->dev_password == NULL) { @@ -69,6 +80,8 @@ struct wps_data * wps_init(const struct wps_config *cfg) 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 */ @@ -76,7 +89,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) if (cfg->pbc) { /* Use special PIN '00000000' for PBC */ data->dev_pw_id = DEV_PW_PUSHBUTTON; - os_free(data->dev_password); + 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); @@ -109,7 +122,8 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->new_ap_settings = os_malloc(sizeof(*data->new_ap_settings)); if (data->new_ap_settings == NULL) { - os_free(data->dev_password); + bin_clear_free(data->dev_password, + data->dev_password_len); os_free(data); return NULL; } @@ -125,6 +139,12 @@ struct wps_data * wps_init(const struct wps_config *cfg) 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; } @@ -154,12 +174,12 @@ void wps_deinit(struct wps_data *data) wpabuf_free(data->dh_pubkey_e); wpabuf_free(data->dh_pubkey_r); wpabuf_free(data->last_msg); - os_free(data->dev_password); - os_free(data->new_psk); + 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); - os_free(data->new_ap_settings); + bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings)); dh5_free(data->dh_ctx); - os_free(data->nfc_pw_token); os_free(data); } @@ -488,17 +508,15 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, wps_build_config_methods(ie, dev->config_methods) || wps_build_uuid_e(ie, uuid) || wps_build_primary_dev_type(dev, ie) || - wps_build_rf_bands(dev, ie) || + wps_build_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) || -#ifdef CONFIG_WPS2 wps_build_manufacturer(dev, ie) || wps_build_model_name(dev, ie) || wps_build_model_number(dev, ie) || wps_build_dev_name(dev, ie) || wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) || -#endif /* CONFIG_WPS2 */ wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) || wps_build_secondary_dev_type(dev, ie) @@ -507,13 +525,6 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, return NULL; } -#ifndef CONFIG_WPS2 - if (dev->p2p && wps_build_dev_name(dev, ie)) { - wpabuf_free(ie); - return NULL; - } -#endif /* CONFIG_WPS2 */ - return wps_ie_encapsulate(ie); } @@ -549,7 +560,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) "wps_state=configured\n"); else ret = 0; - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -557,7 +568,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (attr.ap_setup_locked && *attr.ap_setup_locked) { ret = os_snprintf(pos, end - pos, "wps_ap_setup_locked=1\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -565,7 +576,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (attr.selected_registrar && *attr.selected_registrar) { ret = os_snprintf(pos, end - pos, "wps_selected_registrar=1\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -574,7 +585,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) ret = os_snprintf(pos, end - pos, "wps_device_password_id=%u\n", WPA_GET_BE16(attr.dev_password_id)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -584,7 +595,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) "wps_selected_registrar_config_methods=" "0x%04x\n", WPA_GET_BE16(attr.sel_reg_config_methods)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -596,7 +607,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) wps_dev_type_bin2str(attr.primary_dev_type, devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -615,7 +626,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) str[i] = '\0'; ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str); os_free(str); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -624,10 +635,27 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) ret = os_snprintf(pos, end - pos, "wps_config_methods=0x%04x\n", WPA_GET_BE16(attr.config_methods)); - if (ret < 0 || ret >= end - pos) + 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/src/wps/wps.h b/src/wps/wps.h index c6b7099bfdb8..0a7f65dfd6cb 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2012, Jouni Malinen + * Copyright (c) 2007-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -42,7 +42,6 @@ struct wps_parse_attr; * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets - * @ap_channel: AP channel */ struct wps_credential { u8 ssid[32]; @@ -55,7 +54,6 @@ struct wps_credential { u8 mac_addr[ETH_ALEN]; const u8 *cred_attr; size_t cred_attr_len; - u16 ap_channel; }; #define WPS_DEV_TYPE_LEN 8 @@ -183,6 +181,11 @@ struct wps_config { * 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); @@ -246,14 +249,15 @@ 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 *psk, - size_t psk_len); + 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 @@ -382,6 +386,14 @@ struct wps_registrar_config { * 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; }; @@ -419,6 +431,16 @@ enum wps_event { */ 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 */ @@ -487,11 +509,17 @@ union wps_event_data { 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 { @@ -639,6 +667,16 @@ struct wps_context { */ 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 * @@ -729,6 +767,13 @@ struct wps_context { 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 */ @@ -769,10 +814,12 @@ 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); + 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); @@ -783,9 +830,11 @@ 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); +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); @@ -793,14 +842,22 @@ void wps_er_refresh(struct wps_er *er); void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx); void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods); -int wps_er_pbc(struct wps_er *er, const u8 *uuid); -int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len); -int wps_er_set_config(struct wps_er *er, const u8 *uuid, +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 *pin, - size_t pin_len, const struct wps_credential *cred); -struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid); +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, @@ -810,14 +867,29 @@ 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_build_wifi_hr(void); +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); diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 29aee8eeca94..b689357a280a 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -24,23 +24,46 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) wpa_printf(MSG_DEBUG, "WPS: * Public Key"); wpabuf_free(wps->dh_privkey); - if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->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->wps->ap && - wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) { + } 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"); - wps->dh_privkey = NULL; dh5_free(wps->dh_ctx); wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey); pubkey = wpabuf_zeropad(pubkey, 192); @@ -102,6 +125,8 @@ int wps_build_config_methods(struct wpabuf *msg, u16 methods) 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); @@ -167,6 +192,8 @@ int wps_build_version(struct wpabuf *msg) * 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); @@ -178,9 +205,17 @@ 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) { -#ifdef CONFIG_WPS2 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); @@ -210,10 +245,11 @@ int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, } WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); -#endif /* CONFIG_WPS2 */ #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); @@ -258,9 +294,10 @@ 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) { u16 auth_types = WPS_AUTH_TYPES; -#ifdef CONFIG_WPS2 + /* WPA/WPA2-Enterprise enrollment not supported through WPS */ + auth_types &= ~WPS_AUTH_WPA; + auth_types &= ~WPS_AUTH_WPA2; auth_types &= ~WPS_AUTH_SHARED; -#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); wpabuf_put_be16(msg, 2); @@ -272,9 +309,7 @@ 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) { u16 encr_types = WPS_ENCR_TYPES; -#ifdef CONFIG_WPS2 encr_types &= ~WPS_ENCR_WEP; -#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); wpabuf_put_be16(msg, 2); @@ -356,15 +391,31 @@ int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, 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); - wpabuf_put_data(msg, dev_pw, dev_pw_len); + 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; } @@ -401,3 +452,34 @@ struct wpabuf * wps_ie_encapsulate(struct wpabuf *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/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 3999b1b88108..40bc1ad2d2c5 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -59,6 +59,14 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, } 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); @@ -75,7 +83,7 @@ static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos, const u8 *end = pos + len; u8 id, elen; - while (pos + 2 < end) { + while (pos + 2 <= end) { id = *pos++; elen = *pos++; if (pos + elen > end) @@ -263,10 +271,13 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->dev_password_id = pos; break; case ATTR_OOB_DEVICE_PASSWORD: - if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 + - WPS_OOB_DEVICE_PASSWORD_MIN_LEN || + if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 || len > WPS_OOB_PUBKEY_HASH_LEN + 2 + - WPS_OOB_DEVICE_PASSWORD_LEN) { + 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; @@ -410,22 +421,6 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, } attr->mac_addr = pos; break; - case ATTR_KEY_PROVIDED_AUTO: - if (len != 1) { - wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided " - "Automatically length %u", len); - return -1; - } - attr->key_prov_auto = pos; - break; - case ATTR_802_1X_ENABLED: - if (len != 1) { - wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled " - "length %u", len); - return -1; - } - attr->dot1x_enabled = pos; - break; case ATTR_SELECTED_REGISTRAR: if (len != 1) { wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar" @@ -497,14 +492,6 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->network_key = pos; attr->network_key_len = len; break; - case ATTR_EAP_TYPE: - attr->eap_type = pos; - attr->eap_type_len = len; - break; - case ATTR_EAP_IDENTITY: - attr->eap_identity = pos; - attr->eap_identity_len = len; - break; case ATTR_AP_SETUP_LOCKED: if (len != 1) { wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked " diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h index 88e51a449445..82c4739f61f5 100644 --- a/src/wps/wps_attr_parse.h +++ b/src/wps/wps_attr_parse.h @@ -47,8 +47,6 @@ struct wps_parse_attr { const u8 *network_idx; /* 1 octet */ const u8 *network_key_idx; /* 1 octet */ const u8 *mac_addr; /* ETH_ALEN (6) octets */ - const u8 *key_prov_auto; /* 1 octet (Bool) */ - const u8 *dot1x_enabled; /* 1 octet (Bool) */ const u8 *selected_registrar; /* 1 octet (Bool) */ const u8 *request_type; /* 1 octet */ const u8 *response_type; /* 1 octet */ @@ -57,6 +55,7 @@ struct wps_parse_attr { 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; @@ -77,10 +76,6 @@ struct wps_parse_attr { size_t ssid_len; const u8 *network_key; /* <= 64 octets */ size_t network_key_len; - const u8 *eap_type; /* <= 8 octets */ - size_t eap_type_len; - const u8 *eap_identity; /* <= 64 octets */ - size_t eap_identity_len; const u8 *authorized_macs; /* <= 30 octets */ size_t authorized_macs_len; const u8 *sec_dev_type_list; /* <= 128 octets */ diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c index b81f106e7710..eadb22fe2e78 100644 --- a/src/wps/wps_attr_process.c +++ b/src/wps/wps_attr_process.c @@ -41,7 +41,7 @@ int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN; hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash); - if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) { + if (os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator"); return -1; } @@ -71,7 +71,7 @@ int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg, } hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash); - if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) { + if (os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) { wpa_printf(MSG_DEBUG, "WPS: Invalid KWA"); return -1; } @@ -207,70 +207,6 @@ static int wps_process_cred_mac_addr(struct wps_credential *cred, } -static int wps_process_cred_eap_type(struct wps_credential *cred, - const u8 *eap_type, size_t eap_type_len) -{ - if (eap_type == NULL) - return 0; /* optional attribute */ - - wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len); - - return 0; -} - - -static int wps_process_cred_eap_identity(struct wps_credential *cred, - const u8 *identity, - size_t identity_len) -{ - if (identity == NULL) - return 0; /* optional attribute */ - - wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity", - identity, identity_len); - - return 0; -} - - -static int wps_process_cred_key_prov_auto(struct wps_credential *cred, - const u8 *key_prov_auto) -{ - if (key_prov_auto == NULL) - return 0; /* optional attribute */ - - wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d", - *key_prov_auto); - - return 0; -} - - -static int wps_process_cred_802_1x_enabled(struct wps_credential *cred, - const u8 *dot1x_enabled) -{ - if (dot1x_enabled == NULL) - return 0; /* optional attribute */ - - wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled); - - return 0; -} - - -static int wps_process_cred_ap_channel(struct wps_credential *cred, - const u8 *ap_channel) -{ - if (ap_channel == NULL) - return 0; /* optional attribute */ - - cred->ap_channel = WPA_GET_BE16(ap_channel); - wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel); - - return 0; -} - - static int wps_workaround_cred_key(struct wps_credential *cred) { if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && @@ -310,14 +246,7 @@ int wps_process_cred(struct wps_parse_attr *attr, wps_process_cred_network_key_idx(cred, attr->network_key_idx) || wps_process_cred_network_key(cred, attr->network_key, attr->network_key_len) || - wps_process_cred_mac_addr(cred, attr->mac_addr) || - wps_process_cred_eap_type(cred, attr->eap_type, - attr->eap_type_len) || - wps_process_cred_eap_identity(cred, attr->eap_identity, - attr->eap_identity_len) || - wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) || - wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) || - wps_process_cred_ap_channel(cred, attr->ap_channel)) + wps_process_cred_mac_addr(cred, attr->mac_addr)) return -1; return wps_workaround_cred_key(cred); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 68d9f0a09972..c1ede6a9ea83 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -9,6 +9,8 @@ #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" @@ -16,6 +18,7 @@ #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, @@ -266,7 +269,7 @@ int wps_pin_str_valid(const char *pin) void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, - u16 config_error, u16 error_indication) + u16 config_error, u16 error_indication, const u8 *mac_addr) { union wps_event_data data; @@ -277,20 +280,26 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, 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) +void wps_success_event(struct wps_context *wps, const u8 *mac_addr) { + union wps_event_data data; + if (wps->event_cb == NULL) return; - wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL); + 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) +void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part, + const u8 *mac_addr) { union wps_event_data data; @@ -300,6 +309,7 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part) 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); } @@ -322,9 +332,28 @@ void wps_pbc_timeout_event(struct wps_context *wps) } +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) +struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, + int channel) { struct wps_data data; struct wpabuf *plain; @@ -340,13 +369,41 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps) data.wps = wps; data.auth_type = wps->auth_types; data.encr_type = wps->encr_types; - if (wps_build_version(plain) || - wps_build_cred(&data, plain) || + 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; } @@ -361,8 +418,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, if (data == NULL) return NULL; - if (wps_build_version(data) || - wps_build_oob_dev_pw(data, dev_pw_id, pubkey, + 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 " @@ -433,7 +489,7 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, 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 (ret < 0 || (unsigned int) ret >= buf_len) + if (os_snprintf_error(buf_len, ret)) return NULL; return buf; @@ -475,12 +531,13 @@ u16 wps_config_methods_str2bin(const char *str) if (str == NULL) { /* Default to enabling methods based on build configuration */ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; -#ifdef CONFIG_WPS2 methods |= WPS_CONFIG_VIRT_DISPLAY; -#endif /* CONFIG_WPS2 */ #ifdef CONFIG_WPS_NFC methods |= WPS_CONFIG_NFC_INTERFACE; #endif /* CONFIG_WPS_NFC */ +#ifdef CONFIG_P2P + methods |= WPS_CONFIG_P2PS; +#endif /* CONFIG_P2P */ } else { if (os_strstr(str, "ethernet")) methods |= WPS_CONFIG_ETHERNET; @@ -498,7 +555,6 @@ u16 wps_config_methods_str2bin(const char *str) methods |= WPS_CONFIG_PUSHBUTTON; if (os_strstr(str, "keypad")) methods |= WPS_CONFIG_KEYPAD; -#ifdef CONFIG_WPS2 if (os_strstr(str, "virtual_display")) methods |= WPS_CONFIG_VIRT_DISPLAY; if (os_strstr(str, "physical_display")) @@ -507,7 +563,8 @@ u16 wps_config_methods_str2bin(const char *str) methods |= WPS_CONFIG_VIRT_PUSHBUTTON; if (os_strstr(str, "physical_push_button")) methods |= WPS_CONFIG_PHY_PUSHBUTTON; -#endif /* CONFIG_WPS2 */ + if (os_strstr(str, "p2ps")) + methods |= WPS_CONFIG_P2PS; } return methods; @@ -562,12 +619,59 @@ struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) #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 *priv = NULL, *pub = NULL, *pw, *ret; - void *dh_ctx; + struct wpabuf *pw; u16 val; pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN); @@ -581,31 +685,223 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, return NULL; } - dh_ctx = dh5_init(&priv, &pub); - if (dh_ctx == NULL) { + if (wps_nfc_gen_dh(pubkey, privkey) < 0) { wpabuf_free(pw); return NULL; } - dh5_free(dh_ctx); *id = 0x10 + val % 0xfff0; - wpabuf_free(*pubkey); - *pubkey = pub; - wpabuf_free(*privkey); - *privkey = priv; wpabuf_free(*dev_pw); *dev_pw = pw; - ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw); - if (ndef && ret) { - struct wpabuf *tmp; - tmp = ndef_build_wifi(ret); - wpabuf_free(ret); - if (tmp == NULL) - return NULL; - ret = tmp; + return 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; } - return ret; + 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 + 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/src/wps/wps_defs.h b/src/wps/wps_defs.h index 2f42603a7839..25cd14a0b32a 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -13,15 +13,12 @@ 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 */ -#ifdef CONFIG_WPS2 #define WPS_VERSION 0x20 -#else /* CONFIG_WPS2 */ -#define WPS_VERSION 0x10 -#endif /* CONFIG_WPS2 */ #endif /* CONFIG_WPS_TESTING */ @@ -145,7 +142,8 @@ enum { WFA_ELEM_AUTHORIZEDMACS = 0x01, WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, WFA_ELEM_REQUEST_TO_ENROLL = 0x03, - WFA_ELEM_SETTINGS_DELAY_TIME = 0x04 + WFA_ELEM_SETTINGS_DELAY_TIME = 0x04, + WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05 }; /* Device Password ID */ @@ -155,7 +153,9 @@ enum wps_dev_password_id { DEV_PW_MACHINE_SPECIFIED = 0x0002, DEV_PW_REKEY = 0x0003, DEV_PW_PUSHBUTTON = 0x0004, - DEV_PW_REGISTRAR_SPECIFIED = 0x0005 + DEV_PW_REGISTRAR_SPECIFIED = 0x0005, + DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007, + DEV_PW_P2PS_DEFAULT = 0x0008 }; /* Message Type */ @@ -180,7 +180,7 @@ enum wps_msg_type { /* Authentication Type Flags */ #define WPS_AUTH_OPEN 0x0001 #define WPS_AUTH_WPAPSK 0x0002 -#define WPS_AUTH_SHARED 0x0004 +#define WPS_AUTH_SHARED 0x0004 /* deprecated */ #define WPS_AUTH_WPA 0x0008 #define WPS_AUTH_WPA2 0x0010 #define WPS_AUTH_WPA2PSK 0x0020 @@ -189,7 +189,7 @@ enum wps_msg_type { /* Encryption Type Flags */ #define WPS_ENCR_NONE 0x0001 -#define WPS_ENCR_WEP 0x0002 +#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 | \ @@ -215,7 +215,9 @@ enum wps_config_error { 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_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 */ @@ -223,6 +225,7 @@ 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 }; @@ -240,12 +243,11 @@ enum wps_error_indication { #define WPS_CONFIG_NFC_INTERFACE 0x0040 #define WPS_CONFIG_PUSHBUTTON 0x0080 #define WPS_CONFIG_KEYPAD 0x0100 -#ifdef CONFIG_WPS2 #define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 #define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_P2PS 0x1000 #define WPS_CONFIG_VIRT_DISPLAY 0x2008 #define WPS_CONFIG_PHY_DISPLAY 0x4008 -#endif /* CONFIG_WPS2 */ /* Connection Type Flags */ #define WPS_CONN_ESS 0x01 @@ -279,30 +281,71 @@ enum wps_dev_categ { WPS_DEV_DISPLAY = 7, WPS_DEV_MULTIMEDIA = 8, WPS_DEV_GAMING = 9, - WPS_DEV_PHONE = 10 + 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_PHONE_WINDOWS_MOBILE = 1 + 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, }; diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 3c94a43467a1..0d01211a261c 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -85,8 +85,7 @@ int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg) } -static int wps_build_serial_number(struct wps_device_data *dev, - struct wpabuf *msg) +int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg) { size_t len; wpa_printf(MSG_DEBUG, "WPS: * Serial Number"); @@ -217,13 +216,10 @@ int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg) } -int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg) +int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg, + u8 rf_band) { - wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands); - wpabuf_put_be16(msg, ATTR_RF_BANDS); - wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, dev->rf_bands); - return 0; + return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands); } @@ -257,11 +253,9 @@ static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str, wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len); os_free(dev->manufacturer); - dev->manufacturer = os_malloc(str_len + 1); + dev->manufacturer = dup_binstr(str, str_len); if (dev->manufacturer == NULL) return -1; - os_memcpy(dev->manufacturer, str, str_len); - dev->manufacturer[str_len] = '\0'; return 0; } @@ -278,11 +272,9 @@ static int wps_process_model_name(struct wps_device_data *dev, const u8 *str, wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len); os_free(dev->model_name); - dev->model_name = os_malloc(str_len + 1); + dev->model_name = dup_binstr(str, str_len); if (dev->model_name == NULL) return -1; - os_memcpy(dev->model_name, str, str_len); - dev->model_name[str_len] = '\0'; return 0; } @@ -299,11 +291,9 @@ static int wps_process_model_number(struct wps_device_data *dev, const u8 *str, wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len); os_free(dev->model_number); - dev->model_number = os_malloc(str_len + 1); + dev->model_number = dup_binstr(str, str_len); if (dev->model_number == NULL) return -1; - os_memcpy(dev->model_number, str, str_len); - dev->model_number[str_len] = '\0'; return 0; } @@ -320,11 +310,9 @@ static int wps_process_serial_number(struct wps_device_data *dev, wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len); os_free(dev->serial_number); - dev->serial_number = os_malloc(str_len + 1); + dev->serial_number = dup_binstr(str, str_len); if (dev->serial_number == NULL) return -1; - os_memcpy(dev->serial_number, str, str_len); - dev->serial_number[str_len] = '\0'; return 0; } @@ -341,11 +329,9 @@ static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str, wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len); os_free(dev->device_name); - dev->device_name = os_malloc(str_len + 1); + dev->device_name = dup_binstr(str, str_len); if (dev->device_name == NULL) return -1; - os_memcpy(dev->device_name, str, str_len); - dev->device_name[str_len] = '\0'; return 0; } @@ -418,25 +404,6 @@ int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) } -void wps_device_data_dup(struct wps_device_data *dst, - const struct wps_device_data *src) -{ - if (src->device_name) - dst->device_name = os_strdup(src->device_name); - if (src->manufacturer) - dst->manufacturer = os_strdup(src->manufacturer); - if (src->model_name) - dst->model_name = os_strdup(src->model_name); - if (src->model_number) - dst->model_number = os_strdup(src->model_number); - if (src->serial_number) - dst->serial_number = os_strdup(src->serial_number); - os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); - dst->os_version = src->os_version; - dst->rf_bands = src->rf_bands; -} - - void wps_device_data_free(struct wps_device_data *dev) { os_free(dev->device_name); diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index 200c9c45adcf..c9034addbcc6 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -14,11 +14,13 @@ 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); +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, @@ -28,8 +30,6 @@ int wps_process_device_attrs(struct wps_device_data *dev, struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); -void wps_device_data_dup(struct wps_device_data *dst, - const struct wps_device_data *src); void wps_device_data_free(struct wps_device_data *dev); int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg, diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 837b9412e8ff..89957b1a818a 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -16,16 +16,6 @@ #include "wps_dev_attr.h" -static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS: * MAC Address"); - wpabuf_put_be16(msg, ATTR_MAC_ADDR); - wpabuf_put_be16(msg, ETH_ALEN); - wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN); - return 0; -} - - static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) { u8 state; @@ -140,16 +130,14 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) * workaround. */ config_methods &= ~WPS_CONFIG_PUSHBUTTON; -#ifdef CONFIG_WPS2 config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); -#endif /* CONFIG_WPS2 */ } if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || - wps_build_mac_addr(wps, msg) || + wps_build_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) || @@ -158,7 +146,8 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_config_methods(msg, config_methods) || wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_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) || @@ -186,6 +175,12 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) } 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; @@ -252,17 +247,19 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) { - u16 auth_type = wps->wps->auth_types; + u16 auth_type = wps->wps->ap_auth_type; - /* Select the best authentication 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; - else if (auth_type & WPS_AUTH_SHARED) - auth_type = WPS_AUTH_SHARED; wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type); wpabuf_put_be16(msg, ATTR_AUTH_TYPE); @@ -274,19 +271,18 @@ static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) { - u16 encr_type = wps->wps->encr_types; + u16 encr_type = wps->wps->ap_encr_type; - /* Select the best encryption type */ - if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { + /* + * 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; - } else { - if (encr_type & WPS_ENCR_WEP) - encr_type = WPS_ENCR_WEP; - else if (encr_type & WPS_ENCR_NONE) - encr_type = WPS_ENCR_NONE; } wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type); @@ -299,7 +295,35 @@ static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg) { - wpa_printf(MSG_DEBUG, "WPS: * Network Key"); + 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); @@ -319,6 +343,9 @@ static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg) 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, @@ -326,11 +353,19 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain) return 0; } - return wps_build_cred_ssid(wps, plain) || + 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; } @@ -402,7 +437,7 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) if (wps->wps->ap) wps->state = RECV_ACK; else { - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->peer_dev.mac_addr); wps->state = WPS_FINISHED; } return msg; @@ -523,6 +558,24 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, 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) @@ -588,11 +641,11 @@ static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1) len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); - if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) { + 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_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr); return -1; } @@ -628,11 +681,11 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2) len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); - if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) { + 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_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr); return -1; } @@ -679,7 +732,6 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, #endif /* CONFIG_WPS_STRICT */ } -#ifdef CONFIG_WPS2 if (!(wps->cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { if (wps->cred.encr_type & WPS_ENCR_WEP) { @@ -693,7 +745,6 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, "invalid encr_type 0x%x", wps->cred.encr_type); return -1; } -#endif /* CONFIG_WPS2 */ if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; @@ -780,7 +831,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps, #endif /* CONFIG_WPS_STRICT */ } -#ifdef CONFIG_WPS2 if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) { if (cred.encr_type & WPS_ENCR_WEP) { @@ -794,7 +844,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps, "invalid encr_type 0x%x", cred.encr_type); return -1; } -#endif /* CONFIG_WPS2 */ #ifdef CONFIG_WPS_STRICT if (wps2) { @@ -811,7 +860,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps, } #endif /* CONFIG_WPS_STRICT */ -#ifdef CONFIG_WPS2 if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP) { wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> " @@ -825,7 +873,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps, "WPAPSK+WPA2PSK"); cred.auth_type |= WPS_AUTH_WPA2PSK; } -#endif /* CONFIG_WPS2 */ if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); @@ -837,6 +884,63 @@ static int wps_process_ap_settings_e(struct wps_data *wps, } +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) @@ -852,7 +956,8 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, 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_uuid_r(wps, attr->uuid_r) || + wps_process_dev_pw_id(wps, attr->dev_password_id)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -880,6 +985,38 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, 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; } @@ -1162,7 +1299,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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->error_indication, + wps->peer_dev.mac_addr); break; case WPS_M6: if (wps_validate_m6(msg) < 0) @@ -1170,7 +1308,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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->error_indication, + wps->peer_dev.mac_addr); break; case WPS_M8: if (wps_validate_m8(msg) < 0) @@ -1178,7 +1317,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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->error_indication, + wps->peer_dev.mac_addr); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -1241,7 +1381,7 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps, if (wps->state == RECV_ACK && wps->wps->ap) { wpa_printf(MSG_DEBUG, "WPS: External Registrar registration " "completed successfully"); - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->peer_dev.mac_addr); wps->state = WPS_FINISHED; return WPS_DONE; } @@ -1306,15 +1446,15 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, switch (wps->state) { case RECV_M4: wps_fail_event(wps->wps, WPS_M3, config_error, - wps->error_indication); + 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->error_indication, wps->peer_dev.mac_addr); break; case RECV_M8: wps_fail_event(wps->wps, WPS_M7, config_error, - wps->error_indication); + wps->error_indication, wps->peer_dev.mac_addr); break; default: break; diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index 95a0dec0533c..078ff72781a6 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009-2012, Jouni Malinen + * Copyright (c) 2009-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -97,13 +97,16 @@ static void wps_er_sta_remove_all(struct wps_er_ap *ap) static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, - struct in_addr *addr, const u8 *uuid) + struct in_addr *addr, const u8 *uuid, + const u8 *mac_addr) { struct wps_er_ap *ap; dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && (uuid == NULL || - os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0)) + os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) && + (mac_addr == NULL || + os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0)) return ap; } return NULL; @@ -182,10 +185,8 @@ static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap) dl_list_del(&ap->list); wps_er_ap_free(ap); - if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) { - eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); + if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) wps_er_deinit_finish(er, NULL); - } } @@ -290,7 +291,7 @@ int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr) struct wps_er_ap *ap; struct wps_er_ap_settings *settings; - ap = wps_er_ap_get(er, addr, NULL); + ap = wps_er_ap_get(er, addr, NULL, NULL); if (ap == NULL || ap->ap_settings == NULL) return -1; @@ -578,12 +579,15 @@ static void wps_er_parse_device_description(struct wps_er_ap *ap, wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number); ap->udn = xml_get_first_item(data, "UDN"); - wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); - pos = os_strstr(ap->udn, "uuid:"); - if (pos) { - pos += 5; - if (uuid_str2bin(pos, ap->uuid) < 0) - wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN"); + if (ap->udn) { + wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); + pos = os_strstr(ap->udn, "uuid:"); + if (pos) { + pos += 5; + if (uuid_str2bin(pos, ap->uuid) < 0) + wpa_printf(MSG_DEBUG, + "WPS ER: Invalid UUID in UDN"); + } } ap->upc = xml_get_first_item(data, "UPC"); @@ -636,7 +640,7 @@ void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, { struct wps_er_ap *ap; - ap = wps_er_ap_get(er, addr, uuid); + ap = wps_er_ap_get(er, addr, uuid, NULL); if (ap) { /* Update advertisement timeout */ eloop_cancel_timeout(wps_er_ap_timeout, er, ap); @@ -793,52 +797,31 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, if (attr->manufacturer) { os_free(sta->manufacturer); - sta->manufacturer = os_malloc(attr->manufacturer_len + 1); - if (sta->manufacturer) { - os_memcpy(sta->manufacturer, attr->manufacturer, - attr->manufacturer_len); - sta->manufacturer[attr->manufacturer_len] = '\0'; - } + sta->manufacturer = dup_binstr(attr->manufacturer, + attr->manufacturer_len); } if (attr->model_name) { os_free(sta->model_name); - sta->model_name = os_malloc(attr->model_name_len + 1); - if (sta->model_name) { - os_memcpy(sta->model_name, attr->model_name, - attr->model_name_len); - sta->model_name[attr->model_name_len] = '\0'; - } + sta->model_name = dup_binstr(attr->model_name, + attr->model_name_len); } if (attr->model_number) { os_free(sta->model_number); - sta->model_number = os_malloc(attr->model_number_len + 1); - if (sta->model_number) { - os_memcpy(sta->model_number, attr->model_number, - attr->model_number_len); - sta->model_number[attr->model_number_len] = '\0'; - } + sta->model_number = dup_binstr(attr->model_number, + attr->model_number_len); } if (attr->serial_number) { os_free(sta->serial_number); - sta->serial_number = os_malloc(attr->serial_number_len + 1); - if (sta->serial_number) { - os_memcpy(sta->serial_number, attr->serial_number, - attr->serial_number_len); - sta->serial_number[attr->serial_number_len] = '\0'; - } + sta->serial_number = dup_binstr(attr->serial_number, + attr->serial_number_len); } if (attr->dev_name) { os_free(sta->dev_name); - sta->dev_name = os_malloc(attr->dev_name_len + 1); - if (sta->dev_name) { - os_memcpy(sta->dev_name, attr->dev_name, - attr->dev_name_len); - sta->dev_name[attr->dev_name_len] = '\0'; - } + sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len); } eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); @@ -1289,6 +1272,22 @@ wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) /* Limit event_id to < 32 bits to avoid issues with atoi() */ er->event_id &= 0x0fffffff; + if (filter && os_strncmp(filter, "ifname=", 7) == 0) { + const char *pos, *end; + pos = filter + 7; + end = os_strchr(pos, ' '); + if (end) { + size_t len = end - pos; + os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ? + len + 1 : sizeof(er->ifname)); + filter = end + 1; + } else { + os_strlcpy(er->ifname, pos, sizeof(er->ifname)); + filter = NULL; + } + er->forced_ifname = 1; + } + if (filter) { if (inet_aton(filter, &er->filter_addr) == 0) { wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " @@ -1299,10 +1298,10 @@ wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " "with %s", filter); } - if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, + if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text, er->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " - "for %s. Does it have IP address?", ifname); + "for %s. Does it have IP address?", er->ifname); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1349,9 +1348,19 @@ static void wps_er_deinit_finish(void *eloop_data, void *user_ctx) struct wps_er *er = eloop_data; void (*deinit_done_cb)(void *ctx); void *deinit_done_ctx; + struct wps_er_ap *ap, *tmp; wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); + dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap, + list) { + wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it", + inet_ntoa(ap->addr), ap->location); + dl_list_del(&ap->list); + wps_er_ap_free(ap); + } + + eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); deinit_done_cb = er->deinit_done_cb; deinit_done_ctx = er->deinit_done_ctx; os_free(er->ip_addr_text); @@ -1484,11 +1493,9 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) { -#ifdef CONFIG_WPS2 wpabuf_put_be16(msg, ATTR_UUID_R); wpabuf_put_be16(msg, WPS_UUID_LEN); wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); -#endif /* CONFIG_WPS2 */ return 0; } @@ -1500,9 +1507,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, struct wps_er_ap *ap; struct wps_registrar *reg = er->wps->registrar; const u8 *auth_macs; -#ifdef CONFIG_WPS2 u8 bcast[ETH_ALEN]; -#endif /* CONFIG_WPS2 */ size_t count; union wps_event_data data; @@ -1516,13 +1521,11 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, return; auth_macs = wps_authorized_macs(reg, &count); -#ifdef CONFIG_WPS2 if (count == 0) { os_memset(bcast, 0xff, ETH_ALEN); auth_macs = bcast; count = 1; } -#endif /* CONFIG_WPS2 */ if (wps_build_version(msg) || wps_er_build_selected_registrar(msg, sel_reg) || @@ -1555,7 +1558,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, } -int wps_er_pbc(struct wps_er *er, const u8 *uuid) +int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr) { int res; struct wps_er_ap *ap; @@ -1569,11 +1572,14 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid) return -2; } - ap = wps_er_ap_get(er, NULL, uuid); + if (uuid) + ap = wps_er_ap_get(er, NULL, uuid, NULL); + else + ap = NULL; if (ap == NULL) { struct wps_er_sta *sta = NULL; dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { - sta = wps_er_sta_get(ap, NULL, uuid); + sta = wps_er_sta_get(ap, addr, uuid); if (sta) { uuid = ap->uuid; break; @@ -1619,6 +1625,19 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) } +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr) +{ + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + struct wps_er_sta *sta; + sta = wps_er_sta_get(ap, addr, NULL); + if (sta) + return sta->uuid; + } + return NULL; +} + + static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, enum http_client_event event) { @@ -1877,20 +1896,22 @@ static int wps_er_send_get_device_info(struct wps_er_ap *ap, } -int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len) +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len) { struct wps_er_ap *ap; if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " "request"); return -1; } + if (uuid == NULL) + uuid = ap->uuid; if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " "with the AP - cannot start learn"); @@ -1908,7 +1929,7 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, } -int wps_er_set_config(struct wps_er *er, const u8 *uuid, +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, const struct wps_credential *cred) { struct wps_er_ap *ap; @@ -1916,7 +1937,7 @@ int wps_er_set_config(struct wps_er *er, const u8 *uuid, if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " "request"); @@ -1960,20 +1981,23 @@ static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) } -int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len, 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 wps_er_ap *ap; if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " "request"); return -1; } + if (uuid == NULL) + uuid = ap->uuid; if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " "with the AP - cannot start config"); @@ -1999,16 +2023,39 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, #ifdef CONFIG_WPS_NFC -struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid) + +struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, + struct wps_credential *cred) { - struct wps_er_ap *ap; struct wpabuf *ret; struct wps_data data; + ret = wpabuf_alloc(500); + if (ret == NULL) + return NULL; + + os_memset(&data, 0, sizeof(data)); + data.wps = wps; + data.use_cred = cred; + if (wps_build_cred(&data, ret) || + wps_build_wfa_ext(ret, 0, NULL, 0)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} + + +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, + const u8 *addr) +{ + struct wps_er_ap *ap; + if (er == NULL) return NULL; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) return NULL; if (ap->ap_settings == NULL) { @@ -2017,20 +2064,32 @@ struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid) return NULL; } - ret = wpabuf_alloc(500); - if (ret == NULL) + return wps_er_config_token_from_cred(er->wps, ap->ap_settings); +} + + +struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, + struct wps_context *wps, const u8 *uuid, + const u8 *addr, struct wpabuf *pubkey) +{ + struct wps_er_ap *ap; + + if (er == NULL) return NULL; - os_memset(&data, 0, sizeof(data)); - data.wps = er->wps; - data.use_cred = ap->ap_settings; - if (wps_build_version(ret) || - wps_build_cred(&data, ret) || - wps_build_wfa_ext(ret, 0, NULL, 0)) { - wpabuf_free(ret); + ap = wps_er_ap_get(er, NULL, uuid, addr); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); return NULL; } - return ret; + os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len); + wps->ssid_len = ap->ap_settings->ssid_len; + + return wps_build_nfc_handover_sel(wps, pubkey, addr, 0); } + #endif /* CONFIG_WPS_NFC */ diff --git a/src/wps/wps_er.h b/src/wps/wps_er.h index 611964741e4b..4b48ff6adf3a 100644 --- a/src/wps/wps_er.h +++ b/src/wps/wps_er.h @@ -76,6 +76,7 @@ struct wps_er_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) */ diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c index f9f6e6c1a4b6..e381fecbdc86 100644 --- a/src/wps/wps_er_ssdp.c +++ b/src/wps/wps_er_ssdp.c @@ -166,7 +166,9 @@ int wps_er_ssdp_init(struct wps_er *er) return -1; } - er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr); + er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr, + er->forced_ifname ? + er->ifname : NULL); if (er->multicast_sd < 0) { wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket " "for SSDP"); diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 8110894e3af9..f7154f8734bb 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -71,6 +71,12 @@ struct wps_data { 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 @@ -131,11 +137,14 @@ void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, size_t encr_len); void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, - u16 config_error, u16 error_indication); -void wps_success_event(struct wps_context *wps); -void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); + 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); @@ -166,6 +175,9 @@ 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, @@ -193,11 +205,14 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, 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); +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/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c new file mode 100644 index 000000000000..6800e86db911 --- /dev/null +++ b/src/wps/wps_module_tests.c @@ -0,0 +1,337 @@ +/* + * WPS module tests + * Copyright (c) 2014, Jouni Malinen + * + * 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 "wps_attr_parse.h" + +struct wps_attr_parse_test { + const char *data; + int result; + int extra; +}; + +struct wps_attr_parse_test wps_attr_parse_test_cases[] = { + /* Empty message */ + { "", 0, 0 }, + /* Truncated attribute header */ + { "10", -1, 0 }, + { "1010", -1, 0 }, + { "101000", -1, 0 }, + /* Attribute overflow */ + { "10100001", -1, 0 }, +#ifdef CONFIG_WPS_STRICT + { "10270000001057000101", -1, 0 }, + { "1027000010570001010000000000", -1, 0 }, +#else /* CONFIG_WPS_STRICT */ + /* Network Key workaround */ + { "10270000001057000101", 0, 1 }, + { "10230000001057000101", -1, 0 }, + { "10270000101057000101", -1, 0 }, + /* Mac OS X 10.6 padding workaround */ + { "1027000010570001010000000000", 0, 1 }, + { "1027000010570001010000000000000001000000", -1, 0 }, +#endif /* CONFIG_WPS_STRICT */ + /* Version */ + { "104a000110", 0, 0 }, + { "104a0000", -1, 0 }, + /* Message Type */ + { "1022000101", 0, 0 }, + { "10220000", -1, 0 }, + /* Enrollee Nonce */ + { "101a001000112233445566778899aabbccddeeff", 0, 0 }, + { "101a00111122334455667788990011223344556677", -1, 0 }, + /* Registrar Nonce */ + { "1039001000112233445566778899aabbccddeeff", 0, 0 }, + { "103900111122334455667788990011223344556677", -1, 0 }, + /* UUID-E */ + { "1047001000112233445566778899aabbccddeeff", 0, 0 }, + { "10470000", -1, 0 }, + { "104700111122334455667788990011223344556677", -1, 0 }, + /* UUID-R */ + { "1048001000112233445566778899aabbccddeeff", 0, 0 }, + { "10480000", -1, 0 }, + { "104800111122334455667788990011223344556677", -1, 0 }, + /* Auth Type Flags */ + { "100400021122", 0, 0 }, + { "10040001ff", -1, 0 }, + /* Encr Type Flags */ + { "101000021122", 0, 0 }, + { "10100001ff", -1, 0 }, + /* Connection Type Flags */ + { "100d0001ff", 0, 0 }, + { "100d0002ffff", -1, 0 }, + /* Config Methods */ + { "10080002ffff", 0, 0 }, + { "10080001ff", -1, 0 }, + /* Selected Registrar Config Methods */ + { "10530002ffff", 0, 0 }, + { "10530001ff", -1, 0 }, + /* Primary Device Type */ + { "105400081122334455667788", 0, 0 }, + { "105400111122334455667788990011223344556677", -1, 0 }, + /* RF Bands */ + { "103c0001ff", 0, 0 }, + { "103c0002ffff", -1, 0 }, + /* Association State */ + { "10020002ffff", 0, 0 }, + { "10020001ff", -1, 0 }, + /* Config Error */ + { "100900020001", 0, 0 }, + { "10090001ff", -1, 0 }, + /* Device Password ID */ + { "101200020004", 0, 0 }, + { "10120001ff", -1, 0 }, + /* OOB Device Password */ + { "102c001611223344556677889900112233445566778899000007", 0, 0 }, + { "102c0036112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344", 0, 0 }, + { "102c0001ff", -1, 0 }, + { "102c003711223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455", -1, 0 }, + { "102c002511223344556677889900112233445566778899001122334455667788990011223344556677", -1, 0 }, + /* OS Version */ + { "102d000411223344", 0, 0 }, + { "102d00111122334455667788990011223344556677", -1, 0 }, + /* WPS State */ + { "1044000101", 0, 0 }, + { "10440002ffff", -1, 0 }, + /* Authenticator */ + { "100500081122334455667788", 0, 0 }, + { "10050000", -1, 0 }, + { "100500111122334455667788990011223344556677", -1, 0 }, + /* R-Hash1 */ + { "103d00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 }, + { "103d0000", -1, 0 }, + { "103d0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 }, + /* R-Hash2 */ + { "103e00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 }, + { "103e0000", -1, 0 }, + { "103e0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 }, + /* E-Hash1 */ + { "101400201122334455667788990011223344556677889900112233445566778899001122", 0, 0 }, + { "10140000", -1, 0 }, + { "10140021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 }, + /* E-Hash2 */ + { "101500201122334455667788990011223344556677889900112233445566778899001122", 0, 0 }, + { "10150000", -1, 0 }, + { "10150021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 }, + /* R-SNonce1 */ + { "103f001011223344556677889900112233445566", 0, 0 }, + { "103f0000", -1, 0 }, + { "103f00111122334455667788990011223344556677", -1, 0 }, + /* R-SNonce2 */ + { "1040001011223344556677889900112233445566", 0, 0 }, + { "10400000", -1, 0 }, + { "104000111122334455667788990011223344556677", -1, 0 }, + /* E-SNonce1 */ + { "1016001011223344556677889900112233445566", 0, 0 }, + { "10160000", -1, 0 }, + { "101600111122334455667788990011223344556677", -1, 0 }, + /* E-SNonce2 */ + { "1017001011223344556677889900112233445566", 0, 0 }, + { "10170000", -1, 0 }, + { "101700111122334455667788990011223344556677", -1, 0 }, + /* Key Wrap Authenticator */ + { "101e00081122334455667788", 0, 0 }, + { "101e0000", -1, 0 }, + { "101e0009112233445566778899", -1, 0 }, + /* Authentication Type */ + { "100300020001", 0, 0 }, + { "10030001ff", -1, 0 }, + /* Encryption Type */ + { "100f00020001", 0, 0 }, + { "100f0001ff", -1, 0 }, + /* Network Index */ + { "1026000101", 0, 0 }, + { "10260002ffff", -1, 0 }, + /* Network Key Index */ + { "1028000101", 0, 3 }, + { "10280002ffff", -1, 0 }, + /* MAC Address */ + { "10200006112233445566", 0, 0 }, + { "10200000", -1, 0 }, + { "1020000711223344556677", -1, 0 }, + /* Selected Registrar */ + { "1041000101", 0, 0 }, + { "10410002ffff", -1, 0 }, + /* Request Type */ + { "103a000101", 0, 0 }, + { "103a0002ffff", -1, 0 }, + /* Response Type */ + { "103b000101", 0, 0 }, + { "103b0002ffff", -1, 0 }, + /* Manufacturer */ + { "10210000", 0, 0 }, + /* Model Name */ + { "10230000", 0, 0 }, + /* Model Number */ + { "10240000", 0, 0 }, + /* Serial Number */ + { "10420000", 0, 0 }, + /* Device Name */ + { "10110000", 0, 0 }, + /* Public Key */ + { "10320000", 0, 0 }, + /* Enc Settings */ + { "10180000", 0, 0 }, + /* SSID */ + { "10450000", 0, 0 }, + /* AP Setup Locked */ + { "1057000101", 0, 0 }, + { "10570002ffff", -1, 0 }, + /* Requested Device Type */ + { "106a00081122334455667788", 0, 0 }, + { "106a0000", -1, 0 }, + { "106a0009112233445566778899", -1, 0 }, + /* More than maximum Requested Device Type attributes */ + { "106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788", 0, 4 }, + /* Secondary Device Type List */ + { "105500081122334455667788", 0, 0 }, + { "1055000711223344556677", -1, 0 }, + { "1055008811223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566", -1, 0 }, + /* AP Channel */ + { "100100020001", 0, 0 }, + { "1001000101", -1, 0 }, + /* Skip invalid Vendor Extension */ + { "10490000", 0, 0 }, + { "1049000100", 0, 0 }, + { "104900020000", 0, 0 }, + /* Too long unknown vendor extension */ + { "10490401" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "112233445566778899001122334455667788990011223344556677889900" + "1122334455", -1, 0 }, + /* Maximum unknown vendor extensions */ + { "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA", 0, 5 }, + /* More than maximum unknown vendor extensions */ + { "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA10490003BBBBBB", -1, 0 }, + /* WFA vendor extensions */ + { "1049000300372a", 0, 0 }, + { "1049000400372a00", 0, 0 }, + { "1049000500372a0001", 0, 0 }, + { "1049001600372a0001ff0100020101030101040101ff00fe0101", 0, 6 }, + /* Invalid Version2 length */ + { "1049000500372a0000", -1, 0 }, + /* Invalid Network Key Shareable length */ + { "1049000500372a0200", -1, 0 }, + /* Invalid Requedt To Enroll length */ + { "1049000500372a0300", -1, 0 }, + /* Invalid Settings Delay Time length */ + { "1049000500372a0400", -1, 0 }, + /* More than maximum Credential attributes */ + { "100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000", 0, 2 }, +}; + + +static int wps_attr_parse_tests(void) +{ + struct wps_parse_attr attr; + unsigned int i; + int ret = 0; + + wpa_printf(MSG_INFO, "WPS attribute parsing tests"); + + for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) { + struct wpabuf *buf; + size_t len; + struct wps_attr_parse_test *test = + &wps_attr_parse_test_cases[i]; + + len = os_strlen(test->data) / 2; + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(test->data, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + if (wps_parse_msg(buf, &attr) != test->result) { + wpa_printf(MSG_ERROR, "WPS attribute parsing test %u failed: %s", + i, test->data); + ret = -1; + } + switch (test->extra) { + case 1: + if (!attr.network_key || !attr.ap_setup_locked) + ret = -1; + break; + case 2: + if (attr.num_cred != MAX_CRED_COUNT) + ret = -1; + break; + case 3: + if (!attr.network_key_idx) + ret = -1; + break; + case 4: + if (attr.num_req_dev_type != MAX_REQ_DEV_TYPE_COUNT) + ret = -1; + break; + case 5: + if (attr.num_vendor_ext != MAX_WPS_PARSE_VENDOR_EXT) + ret = -1; + break; + case 6: + if (!attr.version2 || + !attr.authorized_macs || + !attr.network_key_shareable || + !attr.request_to_enroll || + !attr.settings_delay_time) + ret = -1; + break; + } + wpabuf_free(buf); + } + + return ret; +} + + +int wps_module_tests(void) +{ + int ret = 0; + + wpa_printf(MSG_INFO, "WPS module tests"); + + if (wps_attr_parse_tests() < 0) + ret = -1; + + return ret; +} diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index b650a3c0ab36..48b7e1288af0 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2012, Jouni Malinen + * Copyright (c) 2008-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -31,16 +31,18 @@ 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]; + 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); - os_free(token); + bin_clear_free(token, sizeof(*token)); } @@ -82,14 +84,14 @@ struct wps_uuid_pin { #define PIN_LOCKED BIT(0) #define PIN_EXPIRES BIT(1) int flags; - struct os_time expiration; + struct os_reltime expiration; u8 enrollee_addr[ETH_ALEN]; }; static void wps_free_pin(struct wps_uuid_pin *pin) { - os_free(pin->pin); + bin_clear_free(pin->pin, pin->pin_len); os_free(pin); } @@ -113,7 +115,7 @@ struct wps_pbc_session { struct wps_pbc_session *next; u8 addr[ETH_ALEN]; u8 uuid_e[WPS_UUID_LEN]; - struct os_time timestamp; + struct os_reltime timestamp; }; @@ -142,8 +144,8 @@ struct wps_registrar { int pbc; int selected_registrar; - int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk, - size_t psk_len); + 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, @@ -171,6 +173,7 @@ struct wps_registrar { int sel_reg_config_methods_override; int static_wep_only; int dualband; + int force_per_enrollee_psk; struct wps_registrar_device *devices; @@ -182,7 +185,9 @@ struct wps_registrar { u8 p2p_dev_addr[ETH_ALEN]; u8 pbc_ignore_uuid[WPS_UUID_LEN]; - struct os_time pbc_ignore_start; +#ifdef WPS_WORKAROUNDS + struct os_reltime pbc_ignore_start; +#endif /* WPS_WORKAROUNDS */ }; @@ -310,9 +315,9 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg, const u8 *addr, const u8 *uuid_e) { struct wps_pbc_session *pbc, *prev = NULL; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); pbc = reg->pbc_sessions; while (pbc) { @@ -346,7 +351,8 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg, pbc = pbc->next; while (pbc) { - if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { + if (os_reltime_expired(&now, &pbc->timestamp, + WPS_PBC_WALK_TIME)) { prev->next = NULL; wps_free_pbc_sessions(pbc); break; @@ -394,9 +400,9 @@ int wps_registrar_pbc_overlap(struct wps_registrar *reg, int count = 0; struct wps_pbc_session *pbc; struct wps_pbc_session *first = NULL; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap"); @@ -412,9 +418,9 @@ int wps_registrar_pbc_overlap(struct wps_registrar *reg, MAC2STR(pbc->addr)); wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", pbc->uuid_e, WPS_UUID_LEN); - if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) { - wpa_printf(MSG_DEBUG, "WPS: PBC walk time has " - "expired"); + if (os_reltime_expired(&now, &pbc->timestamp, + WPS_PBC_WALK_TIME)) { + wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired"); break; } if (first && @@ -532,7 +538,6 @@ static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg, static void wps_set_pushbutton(u16 *methods, u16 conf_methods) { *methods |= WPS_CONFIG_PUSHBUTTON; -#ifdef CONFIG_WPS2 if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) == WPS_CONFIG_VIRT_PUSHBUTTON) *methods |= WPS_CONFIG_VIRT_PUSHBUTTON; @@ -550,7 +555,6 @@ static void wps_set_pushbutton(u16 *methods, u16 conf_methods) */ *methods |= WPS_CONFIG_PHY_PUSHBUTTON; } -#endif /* CONFIG_WPS2 */ } @@ -562,10 +566,8 @@ static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, return 0; methods = reg->wps->config_methods; methods &= ~WPS_CONFIG_PUSHBUTTON; -#ifdef CONFIG_WPS2 methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); -#endif /* CONFIG_WPS2 */ if (reg->pbc) wps_set_pushbutton(&methods, reg->wps->config_methods); if (reg->sel_reg_config_methods_override >= 0) @@ -588,10 +590,8 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg, * external Registrars. */ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; -#ifdef CONFIG_WPS2 methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); -#endif /* CONFIG_WPS2 */ wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); wpabuf_put_be16(msg, 2); @@ -611,13 +611,11 @@ const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count) { *count = 0; -#ifdef CONFIG_WPS2 while (*count < WPS_MAX_AUTHORIZED_MACS) { if (is_zero_ether_addr(reg->authorized_macs_union[*count])) break; (*count)++; } -#endif /* CONFIG_WPS2 */ return (const u8 *) reg->authorized_macs_union; } @@ -667,6 +665,7 @@ wps_registrar_init(struct wps_context *wps, 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); @@ -677,6 +676,22 @@ wps_registrar_init(struct wps_context *wps, } +void wps_registrar_flush(struct wps_registrar *reg) +{ + if (reg == NULL) + return; + wps_free_pins(®->pins); + wps_free_nfc_pw_tokens(®->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() @@ -687,11 +702,8 @@ void wps_registrar_deinit(struct wps_registrar *reg) return; eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); - wps_free_pins(®->pins); - wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); - wps_free_pbc_sessions(reg->pbc_sessions); + wps_registrar_flush(reg); wpabuf_free(reg->extra_cred); - wps_free_devices(reg->devices); os_free(reg); } @@ -746,7 +758,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, if (timeout) { p->flags |= PIN_EXPIRES; - os_get_time(&p->expiration); + os_get_reltime(&p->expiration); p->expiration.sec += timeout; } @@ -766,7 +778,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, else wps_registrar_add_authorized_mac( reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); - wps_registrar_selected_registrar_changed(reg); + 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, @@ -788,20 +800,20 @@ static void wps_registrar_remove_pin(struct wps_registrar *reg, addr = pin->enrollee_addr; wps_registrar_remove_authorized_mac(reg, addr); wps_remove_pin(pin); - wps_registrar_selected_registrar_changed(reg); + 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_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) { if ((pin->flags & PIN_EXPIRES) && - os_time_before(&pin->expiration, &now)) { + 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); @@ -827,7 +839,7 @@ static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg, { if (dev_pw && pin->pin && (dev_pw_len != pin->pin_len || - os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0)) + 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", @@ -951,7 +963,7 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg) 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); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -999,8 +1011,9 @@ int wps_registrar_button_pushed(struct wps_registrar *reg, 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); + 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, @@ -1014,6 +1027,7 @@ 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); } @@ -1022,7 +1036,7 @@ 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); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -1033,7 +1047,9 @@ void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e, wps_registrar_remove_pbc_session(registrar, uuid_e, NULL); wps_registrar_pbc_completed(registrar); - os_get_time(®istrar->pbc_ignore_start); +#ifdef WPS_WORKAROUNDS + os_get_reltime(®istrar->pbc_ignore_start); +#endif /* WPS_WORKAROUNDS */ os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN); } else { wps_registrar_pin_completed(registrar); @@ -1136,9 +1152,9 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, #ifdef WPS_WORKAROUNDS if (reg->pbc_ignore_start.sec && os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) { - struct os_time now, dur; - os_get_time(&now); - os_time_sub(&now, ®->pbc_ignore_start, &dur); + struct os_reltime now, dur; + os_get_reltime(&now); + os_reltime_sub(&now, ®->pbc_ignore_start, &dur); if (dur.sec >= 0 && dur.sec < 5) { wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation " "based on Probe Request from the Enrollee " @@ -1159,13 +1175,14 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, } -static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr, - const u8 *psk, size_t psk_len) +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, psk, psk_len); + return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk, + psk_len); } @@ -1205,10 +1222,8 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg) if (reg->selected_registrar) { methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; -#ifdef CONFIG_WPS2 methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); -#endif /* CONFIG_WPS2 */ if (reg->pbc) wps_set_pushbutton(&methods, reg->wps->config_methods); } @@ -1263,7 +1278,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_sel_reg_dev_password_id(reg, beacon) || wps_build_sel_reg_config_methods(reg, beacon) || wps_build_sel_pbc_reg_uuid_e(reg, beacon) || - (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon)) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, beacon, 0)) || wps_build_wfa_ext(beacon, 0, auth_macs, count) || wps_build_vendor_ext(®->wps->dev, beacon)) { wpabuf_free(beacon); @@ -1293,7 +1308,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_uuid_e(probe, reg->wps->uuid) || wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || - (reg->dualband && wps_build_rf_bands(®->wps->dev, probe)) || + (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || wps_build_wfa_ext(probe, 0, auth_macs, count) || wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); @@ -1341,7 +1356,7 @@ static int wps_get_dev_password(struct wps_data *wps) const u8 *pin; size_t pin_len = 0; - os_free(wps->dev_password); + bin_clear_free(wps->dev_password, wps->dev_password_len); wps->dev_password = NULL; if (wps->pbc) { @@ -1350,18 +1365,40 @@ static int wps_get_dev_password(struct wps_data *wps) 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"); + "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; @@ -1518,18 +1555,6 @@ static int wps_build_cred_network_key(struct wpabuf *msg, } -static int wps_build_cred_mac_addr(struct wpabuf *msg, - const struct wps_credential *cred) -{ - wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")", - MAC2STR(cred->mac_addr)); - wpabuf_put_be16(msg, ATTR_MAC_ADDR); - wpabuf_put_be16(msg, ETH_ALEN); - wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN); - return 0; -} - - static int wps_build_credential(struct wpabuf *msg, const struct wps_credential *cred) { @@ -1538,7 +1563,7 @@ static int wps_build_credential(struct wpabuf *msg, wps_build_cred_auth_type(msg, cred) || wps_build_cred_encr_type(msg, cred) || wps_build_cred_network_key(msg, cred) || - wps_build_cred_mac_addr(msg, cred)) + wps_build_mac_addr(msg, cred->mac_addr)) return -1; return 0; } @@ -1587,8 +1612,6 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->auth_type = WPS_AUTH_WPAPSK; else if (wps->auth_type & WPS_AUTH_OPEN) wps->auth_type = WPS_AUTH_OPEN; - else if (wps->auth_type & WPS_AUTH_SHARED) - wps->auth_type = WPS_AUTH_SHARED; else { wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x", wps->auth_type); @@ -1608,10 +1631,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) return -1; } } else { - if (wps->encr_type & WPS_ENCR_WEP) - wps->encr_type = WPS_ENCR_WEP; - else if (wps->encr_type & WPS_ENCR_NONE) + 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"); @@ -1628,8 +1653,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) !wps->wps->registrar->disable_auto_conf) { u8 r[16]; /* Generate a random passphrase */ - if (random_get_bytes(r, sizeof(r)) < 0) + 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) @@ -1642,13 +1671,15 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk, wps->new_psk_len); os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len); wps->cred.key_len = wps->new_psk_len; - } else if (wps->use_psk_key && wps->wps->psk_set) { + } 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->network_key) { + } 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; @@ -1660,7 +1691,10 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk = os_malloc(wps->new_psk_len); if (wps->new_psk == NULL) return -1; - if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) { + 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; @@ -1768,6 +1802,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) 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; @@ -1792,19 +1827,47 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_conn_type_flags(wps, msg) || wps_build_config_methods_r(wps->wps->registrar, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_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) || - wps_build_authenticator(wps, 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 = RECV_M3; + wps->state = config_in_m2 ? RECV_DONE : RECV_M3; return msg; } @@ -1833,7 +1896,8 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps) wps_build_conn_type_flags(wps, msg) || wps_build_config_methods_r(wps->wps->registrar, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || - wps_build_rf_bands(&wps->wps->dev, msg) || + wps_build_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) || @@ -2167,11 +2231,11 @@ static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1) len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); - if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) { + 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_pwd_auth_fail_event(wps->wps, 0, 1, wps->mac_addr_e); return -1; } @@ -2207,12 +2271,12 @@ static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2) len[3] = wpabuf_len(wps->dh_pubkey_r); hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash); - if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) { + 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_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e); return -1; } @@ -2514,9 +2578,13 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, 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", @@ -2526,14 +2594,17 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, } #ifdef CONFIG_WPS_NFC - if (wps->dev_pw_id >= 0x10) { + 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) { + if (token && token->peer_pk_hash_known) { wpa_printf(MSG_DEBUG, "WPS: Found matching NFC " "Password Token"); dl_list_del(&token->list); @@ -2541,12 +2612,24 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, addr[0] = attr->public_key; sha256_vector(1, addr, &attr->public_key_len, hash); - if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash, - WPS_OOB_PUBKEY_HASH_LEN) != 0) { + 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"); - return WPS_FAILURE; + 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 */ @@ -2564,7 +2647,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps_pbc_overlap_event(wps->wps); wps_fail_event(wps->wps, WPS_M1, WPS_CFG_MULTIPLE_PBC_DETECTED, - WPS_EI_NO_ERROR); + WPS_EI_NO_ERROR, wps->mac_addr_e); wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } @@ -2894,7 +2977,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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->error_indication, wps->mac_addr_e); break; case WPS_M5: if (wps_validate_m5(msg) < 0) @@ -2902,7 +2985,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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->error_indication, wps->mac_addr_e); break; case WPS_M7: if (wps_validate_m7(msg) < 0) @@ -2910,7 +2993,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps, 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->error_indication, wps->mac_addr_e); break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d", @@ -3056,19 +3139,19 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps, switch (old_state) { case RECV_M3: wps_fail_event(wps->wps, WPS_M2, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case RECV_M5: wps_fail_event(wps->wps, WPS_M4, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case RECV_M7: wps_fail_event(wps->wps, WPS_M6, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; case RECV_DONE: wps_fail_event(wps->wps, WPS_M8, config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); break; default: break; @@ -3164,7 +3247,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, if (wps->new_psk) { if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e, - wps->new_psk, wps->new_psk_len)) { + wps->p2p_dev_addr, wps->new_psk, + wps->new_psk_len)) { wpa_printf(MSG_DEBUG, "WPS: Failed to configure the " "new PSK"); } @@ -3180,7 +3264,9 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->uuid_e, wps->p2p_dev_addr); wps_registrar_pbc_completed(wps->wps->registrar); - os_get_time(&wps->wps->registrar->pbc_ignore_start); +#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 { @@ -3189,7 +3275,7 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, /* TODO: maintain AuthorizedMACs somewhere separately for each ER and * merge them into APs own list.. */ - wps_success_event(wps->wps); + wps_success_event(wps->wps, wps->mac_addr_e); return WPS_DONE; } @@ -3258,7 +3344,7 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, wps->state = SEND_WSC_NACK; wps_fail_event(wps->wps, WPS_WSC_DONE, wps->config_error, - wps->error_indication); + wps->error_indication, wps->mac_addr_e); } return ret; default: @@ -3283,7 +3369,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, "unselect internal Registrar"); reg->selected_registrar = 0; reg->pbc = 0; - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -3355,7 +3441,8 @@ static void wps_registrar_sel_reg_union(struct wps_registrar *reg) * 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) +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg, + u16 dev_pw_id) { wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed"); @@ -3371,15 +3458,14 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) u16 methods; methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; -#ifdef CONFIG_WPS2 methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON | WPS_CONFIG_PHY_PUSHBUTTON); -#endif /* CONFIG_WPS2 */ if (reg->pbc) { reg->sel_reg_dev_password_id_override = DEV_PW_PUSHBUTTON; wps_set_pushbutton(&methods, reg->wps->config_methods); - } + } 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; @@ -3423,7 +3509,7 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, d->dev.model_name ? d->dev.model_name : "", d->dev.model_number ? d->dev.model_number : "", d->dev.serial_number ? d->dev.serial_number : ""); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -3434,8 +3520,7 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, int wps_registrar_config_ap(struct wps_registrar *reg, struct wps_credential *cred) { -#ifdef CONFIG_WPS2 - printf("encr_type=0x%x\n", cred->encr_type); + 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) { @@ -3462,7 +3547,6 @@ int wps_registrar_config_ap(struct wps_registrar *reg, "WPAPSK+WPA2PSK"); cred->auth_type |= WPS_AUTH_WPA2PSK; } -#endif /* CONFIG_WPS2 */ if (reg->wps->cred_cb) return reg->wps->cred_cb(reg->wps->cb_ctx, cred); @@ -3475,23 +3559,39 @@ int wps_registrar_config_ap(struct wps_registrar *reg, 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) + 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(®->nfc_pw_tokens, pw_id); token = os_zalloc(sizeof(*token)); if (token == NULL) return -1; - os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + token->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; - os_memcpy(token->dev_pw, dev_pw, dev_pw_len); - token->dev_pw_len = dev_pw_len; + 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(®->nfc_pw_tokens, &token->list); @@ -3499,12 +3599,15 @@ int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, reg->pbc = 0; wps_registrar_add_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); - wps_registrar_selected_registrar_changed(reg); + 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; } @@ -3517,8 +3620,7 @@ int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, u16 id; size_t dev_pw_len; - if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 + - WPS_OOB_DEVICE_PASSWORD_MIN_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; @@ -3537,7 +3639,7 @@ int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, 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); + dev_pw_len, 0); } @@ -3546,7 +3648,15 @@ void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg, { wps_registrar_remove_authorized_mac(reg, (u8 *) "\xff\xff\xff\xff\xff\xff"); - wps_registrar_selected_registrar_changed(reg); + 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/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 09a46a215a9f..933d7340ee8e 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -227,6 +227,8 @@ void format_date(struct wpabuf *buf) 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, @@ -249,13 +251,16 @@ void format_date(struct wpabuf *buf) * use for constructing UUIDs for subscriptions. Presumably any method from * rfc4122 is good enough; I've chosen random number method. */ -static void uuid_make(u8 uuid[UUID_LEN]) +static int uuid_make(u8 uuid[UUID_LEN]) { - os_get_random(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; } @@ -322,11 +327,9 @@ static void subscr_addr_add_url(struct subscription *s, const char *url, url_len -= 7; /* Make a copy of the string to allow modification during parsing */ - scratch_mem = os_malloc(url_len + 1); + scratch_mem = dup_binstr(url, url_len); if (scratch_mem == NULL) goto fail; - os_memcpy(scratch_mem, url, url_len); - scratch_mem[url_len] = '\0'; wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem); host = scratch_mem; path = os_strchr(host, '/'); @@ -434,23 +437,6 @@ static void subscr_addr_list_create(struct subscription *s, } -int send_wpabuf(int fd, struct wpabuf *buf) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message", - (unsigned long) wpabuf_len(buf)); - errno = 0; - if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) != - (int) wpabuf_len(buf)) { - wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: " - "errno=%d (%s)", - errno, strerror(errno)); - return -1; - } - - return 0; -} - - static void wpabuf_put_property(struct wpabuf *buf, const char *name, const char *value) { @@ -482,14 +468,14 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) "\n" "\n"; const char *format_tail = "\n"; - struct os_time now; + struct os_reltime now; if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } - if (os_get_time(&now) == 0) { + 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; @@ -613,7 +599,10 @@ static struct wpabuf * build_fake_wsc_ack(void) wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); wpabuf_put(msg, WPS_NONCE_LEN); - wps_build_wfa_ext(msg, 0, NULL, 0); + if (wps_build_wfa_ext(msg, 0, NULL, 0)) { + wpabuf_free(msg); + return NULL; + } return msg; } @@ -714,10 +703,12 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) { s = dl_list_first(&sm->subscriptions, struct subscription, list); - wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " - "trashing oldest"); - dl_list_del(&s->list); - subscription_destroy(s); + 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)); @@ -728,7 +719,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, s->sm = sm; s->timeout_time = expire; - uuid_make(s->uuid); + 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 " @@ -984,6 +978,7 @@ static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) 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); @@ -997,7 +992,6 @@ static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) if (sm->multicast_sd >= 0) close(sm->multicast_sd); sm->multicast_sd = -1; - ssdp_listener_stop(sm); sm->started = 0; } diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c index 54ed98f0833f..2949f141220b 100644 --- a/src/wps/wps_upnp_ap.c +++ b/src/wps/wps_upnp_ap.c @@ -22,7 +22,7 @@ static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_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); + wps_registrar_selected_registrar_changed(reg, 0); } @@ -61,17 +61,15 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, os_memcpy(s->authorized_macs, attr.authorized_macs, count * ETH_ALEN); } else if (!attr.version2) { -#ifdef CONFIG_WPS2 wpa_printf(MSG_DEBUG, "WPS: Add broadcast " "AuthorizedMACs for WPS 1.0 ER"); os_memset(s->authorized_macs, 0xff, ETH_ALEN); -#endif /* CONFIG_WPS2 */ } eloop_register_timeout(WPS_PBC_WALK_TIME, 0, upnp_er_set_selected_timeout, s, reg); } - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); return 0; } @@ -83,5 +81,5 @@ void upnp_er_remove_notification(struct wps_registrar *reg, s->selected_registrar = 0; eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg); if (reg) - wps_registrar_selected_registrar_changed(reg); + wps_registrar_selected_registrar_changed(reg, 0); } diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index 7f3c56109e19..f289fe685ac7 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -158,7 +158,6 @@ 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 send_wpabuf(int fd, struct wpabuf *buf); int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, u8 mac[ETH_ALEN]); @@ -171,7 +170,7 @@ 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); +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 */ diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 17a82074a7fb..26a740d25224 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -3,7 +3,7 @@ * Copyright (c) 2000-2003 Intel Corporation * Copyright (c) 2006-2007 Sony Corporation * Copyright (c) 2008-2009 Atheros Communications - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2013, Jouni Malinen * * See wps_upnp.c for more details on licensing and code history. */ @@ -13,6 +13,9 @@ #include #include #include +#ifdef __linux__ +#include +#endif /* __linux__ */ #include "common.h" #include "uuid.h" @@ -131,6 +134,8 @@ next_advertisement(struct upnp_wps_device_sm *sm, *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) @@ -312,7 +317,8 @@ static void advertisement_state_machine_handler(void *eloop_data, * (see notes above) */ next_timeout_msec = 0; - os_get_random((void *) &r, sizeof(r)); + 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++; @@ -584,6 +590,8 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, &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)); @@ -854,7 +862,7 @@ fail: } -int ssdp_open_multicast_sock(u32 ip_addr) +int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname) { int sd; /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet @@ -865,6 +873,22 @@ int ssdp_open_multicast_sock(u32 ip_addr) 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); @@ -924,7 +948,7 @@ int ssdp_open_multicast_sock(u32 ip_addr) */ int ssdp_open_multicast(struct upnp_wps_device_sm *sm) { - sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr); + sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr, NULL); if (sm->multicast_sd < 0) return -1; return 0; diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index ce0bede8342d..b1cf571d8acb 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -179,15 +179,12 @@ static const char *wps_device_xml_postfix = /* 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_sm *sm, +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]; - struct upnp_wps_device_interface *iface; - - iface = dl_list_first(&sm->interfaces, - struct upnp_wps_device_interface, list); wpabuf_put_str(buf, wps_device_xml_prefix); @@ -319,13 +316,15 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, 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 (filename == NULL) - filename = "(null)"; /* just in case */ 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; @@ -393,7 +392,7 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, switch (req) { case GET_DEVICE_XML_FILE: - format_wps_device_xml(sm, buf); + format_wps_device_xml(iface, sm, buf); break; case GET_SCPD_XML_FILE: wpabuf_put_str(buf, wps_scpd_xml); @@ -421,13 +420,14 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); - peer = &iface->peer; wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); - if (iface->ctx->ap_pin == NULL) + 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 @@ -475,6 +475,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, 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 @@ -948,7 +950,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event"); end = os_strchr(h, '\n'); - for (; end != NULL; h = end + 1) { + while (end) { /* Option line by option line */ h = end + 1; end = os_strchr(h, '\n'); @@ -996,13 +998,11 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, h++; len = end - h; os_free(callback_urls); - callback_urls = os_malloc(len + 1); + callback_urls = dup_binstr(h, len); if (callback_urls == NULL) { ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } - os_memcpy(callback_urls, h, len); - callback_urls[len] = 0; continue; } /* SID is only for renewal */ @@ -1157,7 +1157,7 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event"); end = os_strchr(h, '\n'); - for (; end != NULL; h = end + 1) { + while (end) { /* Option line by option line */ h = end + 1; end = os_strchr(h, '\n'); @@ -1175,7 +1175,6 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, ..... } #endif - /* SID is only for renewal */ match = "SID:"; match_len = os_strlen(match); if (os_strncasecmp(h, match, match_len) == 0) { @@ -1198,6 +1197,20 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, 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) { @@ -1211,6 +1224,10 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, 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"); + ret = HTTP_PRECONDITION_FAILED; + goto send_msg; } } else { wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not " diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c index e3662562b40a..1c6a14bce4bc 100644 --- a/src/wps/wps_validate.c +++ b/src/wps/wps_validate.c @@ -267,7 +267,7 @@ static int wps_validate_config_error(const u8 *config_error, int mandatory) return 0; } val = WPA_GET_BE16(config_error); - if (val > 18) { + if (val > 20) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error " "attribute value 0x%04x", val); return -1; @@ -290,7 +290,7 @@ static int wps_validate_dev_password_id(const u8 *dev_password_id, return 0; } val = WPA_GET_BE16(dev_password_id); - if (val >= 0x0006 && val <= 0x000f) { + if (val >= 0x0008 && val <= 0x000f) { wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID " "attribute value 0x%04x", val); return -1; -- cgit v1.3