From 6e6d0eb51ef7b7487340bae7f20097ee5a57dbf4 Mon Sep 17 00:00:00 2001 From: Cy Schubert Date: Mon, 22 Apr 2019 15:42:53 +0000 Subject: [PATCH] Import wpa_supplicant/hostapd 2.8 --- CONTRIBUTIONS | 2 +- COPYING | 2 +- README | 2 +- hostapd/Android.mk | 9 + hostapd/ChangeLog | 55 + hostapd/Makefile | 12 + hostapd/README | 2 +- hostapd/README-MULTI-AP | 160 ++ hostapd/android.config | 3 + hostapd/config_file.c | 250 ++- hostapd/ctrl_iface.c | 153 +- hostapd/defconfig | 10 +- hostapd/hostapd.conf | 195 ++- hostapd/hostapd.wpa_psk | 6 + hostapd/hostapd_cli.c | 31 +- hostapd/main.c | 11 +- hostapd/wps-ap-nfc.py | 62 +- hs20/client/.gitignore | 1 + hs20/client/Makefile | 5 + hs20/client/est.c | 13 + hs20/client/osu_client.c | 8 +- src/ap/acs.c | 92 +- src/ap/ap_config.c | 124 +- src/ap/ap_config.h | 31 +- src/ap/ap_drv_ops.h | 18 + src/ap/authsrv.c | 16 +- src/ap/beacon.c | 22 +- src/ap/ctrl_iface_ap.c | 14 +- src/ap/dfs.c | 15 +- src/ap/dhcp_snoop.c | 18 +- src/ap/dpp_hostapd.c | 756 ++------- src/ap/dpp_hostapd.h | 6 - src/ap/drv_callbacks.c | 72 +- src/ap/eap_user_db.c | 12 +- src/ap/fils_hlp.c | 13 + src/ap/hostapd.c | 174 +- src/ap/hostapd.h | 14 +- src/ap/hs20.c | 17 +- src/ap/hw_features.c | 44 +- src/ap/ieee802_11.c | 663 ++++++-- src/ap/ieee802_11.h | 15 +- src/ap/ieee802_11_auth.c | 14 +- src/ap/ieee802_11_he.c | 31 + src/ap/ieee802_11_shared.c | 302 +++- src/ap/ieee802_11_vht.c | 23 + src/ap/ieee802_1x.c | 107 +- src/ap/neighbor_db.c | 123 +- src/ap/neighbor_db.h | 3 +- src/ap/rrm.c | 2 +- src/ap/sta_info.c | 65 +- src/ap/sta_info.h | 10 + src/ap/vlan_full.c | 85 +- src/ap/vlan_init.c | 10 +- src/ap/wnm_ap.c | 90 +- src/ap/wpa_auth.c | 401 ++++- src/ap/wpa_auth.h | 25 +- src/ap/wpa_auth_ft.c | 155 +- src/ap/wpa_auth_glue.c | 94 +- src/ap/wpa_auth_i.h | 13 +- src/ap/wpa_auth_ie.c | 87 +- src/ap/wpa_auth_ie.h | 4 + src/ap/wps_hostapd.c | 68 +- src/common/common_module_tests.c | 178 ++- src/common/defs.h | 32 +- src/common/dpp.c | 1096 ++++++++++++- src/common/dpp.h | 72 +- src/common/hw_features_common.c | 91 +- src/common/hw_features_common.h | 5 + src/common/ieee802_11_common.c | 247 +-- src/common/ieee802_11_common.h | 70 +- src/common/ieee802_11_defs.h | 190 ++- src/common/linux_bridge.h | 15 + src/common/ocv.c | 172 ++ src/common/ocv.h | 40 + src/common/qca-vendor.h | 471 +++++- src/common/sae.c | 345 ++-- src/common/sae.h | 2 + src/common/version.h | 2 +- src/common/wpa_common.c | 57 +- src/common/wpa_common.h | 15 +- src/common/wpa_ctrl.c | 23 +- src/crypto/Makefile | 2 + src/crypto/aes-internal-enc.c | 4 + src/crypto/crypto.h | 9 +- src/crypto/crypto_gnutls.c | 43 +- src/crypto/crypto_internal-modexp.c | 41 +- src/crypto/crypto_internal.c | 3 + src/crypto/crypto_libtomcrypt.c | 5 + src/crypto/crypto_linux.c | 3 + src/crypto/crypto_nettle.c | 36 +- src/crypto/crypto_openssl.c | 122 +- src/crypto/crypto_wolfssl.c | 15 +- src/crypto/dh_groups.c | 1 + src/crypto/md4-internal.c | 2 +- src/crypto/random.c | 74 +- src/crypto/sha1-tlsprf.c | 3 - src/crypto/sha512-internal.c | 8 +- src/crypto/sha512.c | 104 ++ src/crypto/tls.h | 35 +- src/crypto/tls_gnutls.c | 75 +- src/crypto/tls_internal.c | 46 +- src/crypto/tls_none.c | 5 +- src/crypto/tls_openssl.c | 524 ++++++- src/crypto/tls_wolfssl.c | 46 +- src/drivers/driver.h | 121 +- src/drivers/driver_atheros.c | 3 +- src/drivers/driver_bsd.c | 2 +- src/drivers/driver_common.c | 22 +- src/drivers/driver_hostap.c | 12 +- src/drivers/driver_macsec_linux.c | 163 +- src/drivers/driver_nl80211.c | 282 +++- src/drivers/driver_nl80211.h | 1 + src/drivers/driver_nl80211_capa.c | 226 ++- src/drivers/driver_nl80211_event.c | 102 +- src/drivers/driver_nl80211_scan.c | 20 +- src/drivers/driver_openbsd.c | 3 +- src/drivers/driver_roboswitch.c | 36 +- src/drivers/driver_wext.c | 13 +- src/drivers/drivers.mak | 72 +- src/drivers/drivers.mk | 42 +- src/drivers/linux_ioctl.c | 21 +- src/drivers/nl80211_copy.h | 602 ++++++- src/eap_common/eap_eke_common.c | 2 +- src/eap_common/eap_pwd_common.c | 302 ++-- src/eap_common/eap_pwd_common.h | 6 + src/eap_common/eap_sake_common.c | 75 +- src/eap_common/eap_sake_common.h | 8 +- src/eap_peer/eap_config.h | 99 +- src/eap_peer/eap_fast.c | 28 +- src/eap_peer/eap_mschapv2.c | 10 +- src/eap_peer/eap_peap.c | 91 +- src/eap_peer/eap_pwd.c | 93 +- src/eap_peer/eap_sake.c | 12 +- src/eap_peer/eap_tls.c | 1 + src/eap_peer/eap_tls_common.c | 87 +- src/eap_peer/eap_tls_common.h | 3 +- src/eap_peer/eap_ttls.c | 48 +- src/eap_peer/eap_wsc.c | 3 + src/eap_server/eap.h | 3 + src/eap_server/eap_i.h | 1 + src/eap_server/eap_server.c | 31 +- src/eap_server/eap_server_aka.c | 4 + src/eap_server/eap_server_gpsk.c | 4 +- src/eap_server/eap_server_mschapv2.c | 10 +- src/eap_server/eap_server_pax.c | 65 +- src/eap_server/eap_server_peap.c | 58 +- src/eap_server/eap_server_pwd.c | 119 +- src/eap_server/eap_server_sake.c | 42 +- src/eap_server/eap_server_sim.c | 3 + src/eap_server/eap_server_tls.c | 21 + src/eap_server/eap_server_tls_common.c | 31 +- src/eap_server/eap_server_ttls.c | 6 +- src/eap_server/eap_tls_common.h | 3 +- src/eapol_supp/eapol_supp_sm.c | 13 +- src/fst/fst.h | 16 +- src/lib.rules | 5 + src/p2p/p2p.c | 34 +- src/p2p/p2p.h | 15 +- src/p2p/p2p_build.c | 2 +- src/p2p/p2p_group.c | 3 +- src/p2p/p2p_i.h | 4 +- src/p2p/p2p_invitation.c | 2 +- src/p2p/p2p_utils.c | 23 +- src/pae/ieee802_1x_cp.c | 36 +- src/pae/ieee802_1x_cp.h | 1 - src/pae/ieee802_1x_kay.c | 962 +++++++++--- src/pae/ieee802_1x_kay.h | 8 +- src/pae/ieee802_1x_kay_i.h | 52 +- src/pae/ieee802_1x_key.c | 89 +- src/pae/ieee802_1x_key.h | 26 +- src/pae/ieee802_1x_secy_ops.c | 25 +- src/pae/ieee802_1x_secy_ops.h | 2 + src/radius/radius_client.c | 86 +- src/radius/radius_server.c | 255 ++- src/radius/radius_server.h | 1 + src/rsn_supp/pmksa_cache.c | 3 +- src/rsn_supp/tdls.c | 2 +- src/rsn_supp/wpa.c | 283 +++- src/rsn_supp/wpa.h | 12 +- src/rsn_supp/wpa_ft.c | 43 + src/rsn_supp/wpa_i.h | 18 +- src/rsn_supp/wpa_ie.c | 13 + src/rsn_supp/wpa_ie.h | 4 + src/tls/asn1.c | 8 + src/tls/bignum.c | 4 +- src/tls/tlsv1_client.c | 34 +- src/tls/tlsv1_client.h | 3 +- src/tls/tlsv1_client_read.c | 2 +- src/tls/tlsv1_client_write.c | 3 + src/tls/tlsv1_server.c | 57 +- src/tls/tlsv1_server.h | 7 +- src/tls/tlsv1_server_i.h | 2 + src/tls/tlsv1_server_read.c | 51 +- src/tls/tlsv1_server_write.c | 5 +- src/tls/x509v3.c | 2 + src/utils/Makefile | 1 + src/utils/base64.c | 5 +- src/utils/browser.c | 3 +- src/utils/common.c | 36 +- src/utils/common.h | 1 + src/utils/const_time.h | 191 +++ src/utils/eloop.c | 29 +- src/utils/http_curl.c | 35 +- src/utils/json.c | 7 + src/utils/list.h | 6 +- src/utils/os_internal.c | 16 - src/utils/os_none.c | 6 - src/utils/os_unix.c | 12 +- src/utils/utils_module_tests.c | 290 ++++ src/utils/wpa_debug.c | 6 + src/wps/wps.c | 8 +- src/wps/wps.h | 38 + src/wps/wps_attr_build.c | 14 +- src/wps/wps_attr_parse.c | 11 + src/wps/wps_attr_parse.h | 1 + src/wps/wps_common.c | 16 +- src/wps/wps_defs.h | 3 +- src/wps/wps_dev_attr.c | 8 + src/wps/wps_dev_attr.h | 1 + src/wps/wps_enrollee.c | 14 +- src/wps/wps_er.c | 4 +- src/wps/wps_i.h | 5 +- src/wps/wps_registrar.c | 88 +- src/wps/wps_upnp.c | 2 +- src/wps/wps_validate.c | 2 +- wpa_supplicant/Android.mk | 48 +- wpa_supplicant/ChangeLog | 69 + wpa_supplicant/Makefile | 68 +- wpa_supplicant/README | 4 +- wpa_supplicant/README-DPP | 195 +++ wpa_supplicant/README-P2P | 6 +- wpa_supplicant/android.config | 16 +- wpa_supplicant/ap.c | 59 +- wpa_supplicant/bss.c | 9 +- wpa_supplicant/bss.h | 3 +- wpa_supplicant/config.c | 130 +- wpa_supplicant/config.h | 49 + wpa_supplicant/config_file.c | 34 +- wpa_supplicant/config_ssid.h | 76 +- wpa_supplicant/config_winreg.c | 6 + wpa_supplicant/ctrl_iface.c | 230 ++- wpa_supplicant/ctrl_iface_unix.c | 12 +- wpa_supplicant/dbus/Makefile | 4 - wpa_supplicant/dbus/dbus-wpa_supplicant.conf | 8 - wpa_supplicant/dbus/dbus_common.c | 8 - wpa_supplicant/dbus/dbus_new.c | 342 +++- wpa_supplicant/dbus/dbus_new.h | 27 + wpa_supplicant/dbus/dbus_new_handlers.c | 445 +++++- wpa_supplicant/dbus/dbus_new_handlers.h | 18 + wpa_supplicant/dbus/dbus_new_handlers_p2p.c | 97 +- wpa_supplicant/dbus/dbus_new_handlers_p2p.h | 1 + wpa_supplicant/dbus/dbus_new_handlers_wps.c | 10 +- wpa_supplicant/dbus/dbus_old.c | 745 --------- wpa_supplicant/dbus/dbus_old.h | 142 -- wpa_supplicant/dbus/dbus_old_handlers.c | 1393 ----------------- wpa_supplicant/dbus/dbus_old_handlers.h | 101 -- wpa_supplicant/dbus/dbus_old_handlers_wps.c | 152 -- ...fi.epitest.hostap.WPASupplicant.service.in | 5 - wpa_supplicant/defconfig | 77 +- wpa_supplicant/doc/docbook/eapol_test.8 | 4 +- wpa_supplicant/doc/docbook/eapol_test.sgml | 2 +- wpa_supplicant/doc/docbook/wpa_background.8 | 4 +- .../doc/docbook/wpa_background.sgml | 2 +- wpa_supplicant/doc/docbook/wpa_cli.8 | 4 +- wpa_supplicant/doc/docbook/wpa_cli.sgml | 2 +- wpa_supplicant/doc/docbook/wpa_gui.8 | 4 +- wpa_supplicant/doc/docbook/wpa_gui.sgml | 2 +- wpa_supplicant/doc/docbook/wpa_passphrase.8 | 4 +- .../doc/docbook/wpa_passphrase.sgml | 2 +- wpa_supplicant/doc/docbook/wpa_priv.8 | 4 +- wpa_supplicant/doc/docbook/wpa_priv.sgml | 2 +- wpa_supplicant/doc/docbook/wpa_supplicant.8 | 22 +- .../doc/docbook/wpa_supplicant.conf.5 | 2 +- .../doc/docbook/wpa_supplicant.sgml | 30 +- wpa_supplicant/dpp_supplicant.c | 831 +++------- wpa_supplicant/dpp_supplicant.h | 10 - wpa_supplicant/driver_i.h | 24 + wpa_supplicant/eapol_test.c | 3 +- wpa_supplicant/eapol_test.py | 2 +- wpa_supplicant/events.c | 224 ++- wpa_supplicant/examples/dbus-listen-preq.py | 20 +- wpa_supplicant/examples/dpp-qrcode.py | 36 +- wpa_supplicant/examples/p2p-nfc.py | 168 +- wpa_supplicant/examples/p2p/p2p_connect.py | 70 +- wpa_supplicant/examples/p2p/p2p_disconnect.py | 30 +- wpa_supplicant/examples/p2p/p2p_find.py | 34 +- wpa_supplicant/examples/p2p/p2p_flush.py | 30 +- wpa_supplicant/examples/p2p/p2p_group_add.py | 50 +- wpa_supplicant/examples/p2p/p2p_invite.py | 44 +- wpa_supplicant/examples/p2p/p2p_listen.py | 32 +- wpa_supplicant/examples/p2p/p2p_stop_find.py | 32 +- .../examples/wpas-dbus-new-getall.py | 29 +- .../examples/wpas-dbus-new-signals.py | 34 +- wpa_supplicant/examples/wpas-dbus-new-wps.py | 16 +- wpa_supplicant/examples/wpas-dbus-new.py | 20 +- wpa_supplicant/examples/wpas-test.py | 91 -- wpa_supplicant/examples/wps-nfc.py | 124 +- wpa_supplicant/gas_query.c | 2 +- wpa_supplicant/gas_query.h | 1 + wpa_supplicant/hs20_supplicant.c | 31 +- wpa_supplicant/hs20_supplicant.h | 4 +- wpa_supplicant/ibss_rsn.c | 6 +- wpa_supplicant/interworking.c | 7 +- wpa_supplicant/main.c | 12 +- wpa_supplicant/mbo.c | 36 +- wpa_supplicant/mesh.c | 237 +-- wpa_supplicant/mesh_mpm.c | 75 +- wpa_supplicant/mesh_rsn.c | 17 +- wpa_supplicant/notify.c | 81 +- wpa_supplicant/notify.h | 5 + wpa_supplicant/op_classes.c | 68 +- wpa_supplicant/p2p_supplicant.c | 217 ++- wpa_supplicant/p2p_supplicant.h | 9 +- wpa_supplicant/rrm.c | 227 ++- wpa_supplicant/scan.c | 16 +- wpa_supplicant/sme.c | 260 ++- wpa_supplicant/sme.h | 5 + .../systemd/wpa_supplicant.service.in | 4 +- wpa_supplicant/utils/log2pcap.py | 2 +- wpa_supplicant/wmm_ac.c | 11 +- wpa_supplicant/wnm_sta.c | 88 +- wpa_supplicant/wpa_cli.c | 131 +- wpa_supplicant/wpa_supplicant.c | 374 ++++- wpa_supplicant/wpa_supplicant.conf | 100 +- wpa_supplicant/wpa_supplicant_i.h | 45 +- wpa_supplicant/wpas_glue.c | 12 +- wpa_supplicant/wpas_kay.c | 23 +- wpa_supplicant/wps_supplicant.c | 18 +- wpa_supplicant/wps_supplicant.h | 2 +- 329 files changed, 16732 insertions(+), 7283 deletions(-) create mode 100644 hostapd/README-MULTI-AP create mode 100644 hs20/client/.gitignore create mode 100644 src/common/ocv.c create mode 100644 src/common/ocv.h create mode 100644 src/crypto/sha512.c create mode 100644 src/utils/const_time.h create mode 100644 wpa_supplicant/README-DPP delete mode 100644 wpa_supplicant/dbus/dbus_old.c delete mode 100644 wpa_supplicant/dbus/dbus_old.h delete mode 100644 wpa_supplicant/dbus/dbus_old_handlers.c delete mode 100644 wpa_supplicant/dbus/dbus_old_handlers.h delete mode 100644 wpa_supplicant/dbus/dbus_old_handlers_wps.c delete mode 100644 wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in delete mode 100755 wpa_supplicant/examples/wpas-test.py diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS index 053e8ecda90f..c81ad640995a 100644 --- a/CONTRIBUTIONS +++ b/CONTRIBUTIONS @@ -140,7 +140,7 @@ The license terms used for hostap.git files Modified BSD license (no advertisement clause): -Copyright (c) 2002-2018, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. Redistribution and use in source and binary forms, with or without diff --git a/COPYING b/COPYING index 55815d4016a3..5d0115c9ca6f 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2018, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. diff --git a/README b/README index 6586d72ea039..a9f806967bf9 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2018, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. These programs are licensed under the BSD license (the one with diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 322f6a63255c..57a894cc7f8c 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -235,6 +235,12 @@ L_CFLAGS += -DCONFIG_SUITEB192 NEED_SHA384=y endif +ifdef CONFIG_OCV +L_CFLAGS += -DCONFIG_OCV +OBJS += src/common/ocv.c +CONFIG_IEEE80211W=y +endif + ifdef CONFIG_IEEE80211W L_CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -548,6 +554,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS=y NEED_BASE64=y +ifdef CONFIG_DPP2 +L_CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_EAP_IKEV2 diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index f1366b4a9542..327ee3b46ede 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -1,5 +1,60 @@ ChangeLog for hostapd +2019-04-21 - v2.8 + * SAE changes + - added support for SAE Password Identifier + - changed default configuration to enable only group 19 + (i.e., disable groups 20, 21, 25, 26 from default configuration) and + disable all unsuitable groups completely based on REVmd changes + - improved anti-clogging token mechanism and SAE authentication + frame processing during heavy CPU load; this mitigates some issues + with potential DoS attacks trying to flood an AP with large number + of SAE messages + - added Finite Cyclic Group field in status code 77 responses + - reject use of unsuitable groups based on new implementation guidance + in REVmd (allow only FFC groups with prime >= 3072 bits and ECC + groups with prime >= 256) + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-1/] (CVE-2019-9494) + - fixed confirm message validation in error cases + [https://w1.fi/security/2019-3/] (CVE-2019-9496) + * EAP-pwd changes + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-2/] (CVE-2019-9495) + - verify peer scalar/element + [https://w1.fi/security/2019-4/] (CVE-2019-9497 and CVE-2019-9498) + - fix message reassembly issue with unexpected fragment + [https://w1.fi/security/2019-5/] + - enforce rand,mask generation rules more strictly + - fix a memory leak in PWE derivation + - disallow ECC groups with a prime under 256 bits (groups 25, 26, and + 27) + * Hotspot 2.0 changes + - added support for release number 3 + - reject release 2 or newer association without PMF + * added support for RSN operating channel validation + (CONFIG_OCV=y and configuration parameter ocv=1) + * added Multi-AP protocol support + * added FTM responder configuration + * fixed build with LibreSSL + * added FT/RRB workaround for short Ethernet frame padding + * fixed KEK2 derivation for FILS+FT + * added RSSI-based association rejection from OCE + * extended beacon reporting functionality + * VLAN changes + - allow local VLAN management with remote RADIUS authentication + - add WPA/WPA2 passphrase/PSK -based VLAN assignment + * OpenSSL: allow systemwide policies to be overridden + * extended PEAP to derive EMSK to enable use with ERP/FILS + * extended WPS to allow SAE configuration to be added automatically + for PSK (wps_cred_add_sae=1) + * fixed FT and SA Query Action frame with AP-MLME-in-driver cases + * OWE: allow Diffie-Hellman Parameter element to be included with DPP + in preparation for DPP protocol extension + * RADIUS server: started to accept ERP keyName-NAI as user identity + automatically without matching EAP database entry + * fixed PTK rekeying with FILS and FT + 2018-12-02 - v2.7 * fixed WPA packet number reuse with replayed messages and key reinstallation diff --git a/hostapd/Makefile b/hostapd/Makefile index 2ce8b7ded842..6e263c5a4afc 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -278,6 +278,12 @@ CFLAGS += -DCONFIG_SUITEB192 NEED_SHA384=y endif +ifdef CONFIG_OCV +CFLAGS += -DCONFIG_OCV +OBJS += ../src/common/ocv.o +CONFIG_IEEE80211W=y +endif + ifdef CONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -582,6 +588,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS=y NEED_BASE64=y +ifdef CONFIG_DPP2 +CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_EAP_IKEV2 @@ -1095,6 +1104,9 @@ endif ifdef CONFIG_NO_RANDOM_POOL CFLAGS += -DCONFIG_NO_RANDOM_POOL else +ifdef CONFIG_GETRANDOM +CFLAGS += -DCONFIG_GETRANDOM +endif OBJS += ../src/crypto/random.o HOBJS += ../src/crypto/random.o HOBJS += ../src/utils/eloop.o diff --git a/hostapd/README b/hostapd/README index ae5317698ee8..1f30d7ea39fa 100644 --- a/hostapd/README +++ b/hostapd/README @@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2018, Jouni Malinen and contributors +Copyright (c) 2002-2019, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with diff --git a/hostapd/README-MULTI-AP b/hostapd/README-MULTI-AP new file mode 100644 index 000000000000..ccee69e13c08 --- /dev/null +++ b/hostapd/README-MULTI-AP @@ -0,0 +1,160 @@ +hostapd, wpa_supplicant and the Multi-AP Specification +====================================================== + +This document describes how hostapd and wpa_supplicant can be configured to +support the Multi-AP Specification. + +Introduction to Multi-AP +------------------------ + +The Wi-Fi Alliance Multi-AP Specification is the technical specification for +Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi AllianceĀ® certification program for +Multi-AP. It defines control protocols between Wi-FiĀ® access points (APs) to +join them into a network with centralized control and operation. It is targeted +only at routers (repeaters, gateways, ...), not at clients. Clients are not +involved at all in the protocols. + +Most of the Multi-AP specification falls outside of the scope of +hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items +summarized below. The rest of the protocol must be implemented by a separate +daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd, +e.g., to get a list of associated clients, but this can be done using the normal +hostapd interfaces. + +hostapd/wpa_supplicant needs to be configured specifically to support: +- the WPS onboarding process; +- configuring backhaul links. + +The text below refers to "Multi-AP Specification v1.0" [3]. + + +Fronthaul and backhaul links +---------------------------- + +In a Multi-AP network, the central controller can configure the BSSs on the +devices that are joined into the network. These are called fronthaul BSSs. +From the point of view of hostapd, there is nothing special about these +fronthaul BSSs. + +In addition to fronthaul BSSs, the controller can also configure backhaul +links. A backhaul link is a link between two access point devices, giving +internet access to access point devices that don't have a wired link. The +Multi-AP specification doesn't dictate this, but typically the backhaul link +will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the +wired Ethernet ports. + +A backhaul link must be treated specially by hostapd and wpa_supplicant. One +side of the backhaul link is configured through the Multi-AP protocol as the +"backhaul STA", i.e., the client side of the link. A backhaul STA is like any +station and is handled appropriately by wpa_supplicant, but two additional +features are required. It must send an additional information element in each +(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it +must use 4-address mode for all frames sent over this link ([3], section 14). +Therefore, wpa_supplicant must be configured explicitly as the backhaul STA +role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block +or when configuring the network profile through the control interface. When +'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in +(Re)Association Request frame and verifies that it is included in the +(Re)Association Response frame. If it is not, association fails. If it is, +wpa_supplicant sets 4-address mode for this interface through a driver +callback. + +The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must +be handled specially by hostapd, because it must add an additional information +element in each (Re)Association Response frame, but only to stations that have +identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6). +This is important because it is possible to use the same BSS and SSID for +fronthaul and backhaul at the same time. The additional information element must +only be used for frames sent to a backhaul STA, not to a normal STA. Also, +frames sent to a backhaul STA must use 4-address mode, while frames sent to a +normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use +3-address mode. + +A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap' +configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3 +(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd +parses the Multi-AP information element in the Association Request frame. If the +station is a backhaul STA and the BSS is configured as a backhaul BSS, +hostapd sets up 4-address mode. Since there may be multiple stations connected +simultaneously, and each of them has a different RA (receiver address), a VLAN +is created for each backhaul STA and it is automatically added to a bridge. +This is the same behavior as for WDS, and the relevant option ('bridge' or +'wds_bridge') applies here as well. + +If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate +without the Multi-AP information element will be denied. + +If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate +with the Multi-AP information element will be denied. That is also the only +difference with 'multi_ap' set to 0: in the latter case, the Multi-AP +information element is simply ignored. + +In summary, this is the end-to-end behavior for a backhaul BSS (i.e., +multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in +hostapd on AP). Note that point 1 means that hostapd must not be configured +with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for +that. + +1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing + Multi-AP specific). +2. STA sends Authentication frame (nothing Multi-AP specific). +3. AP sends Authentication frame (nothing Multi-AP specific). +4. STA sends Association Request frame with Multi-AP IE. +5. AP sends Association Response frame with Multi-AP IE. +6. STA and AP both use 4-address mode for Data frames. + + +WPS support +----------- + +WPS requires more special handling. WPS must only be advertised on fronthaul +BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only +BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS. +When a WPS M1 message has an additional subelement that indicates a request for +a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul +BSS credentials; instead, it should respond with the (potentially different) +backhaul BSS credentials. + +To support this, hostapd has the 'multi_ap_backhaul_ssid', +'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options. +When these are set on an BSS with WPS, they are used instead of the normal +credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only +WPA2-Personal is supported in the Multi-AP specification, so there is no need +to specify authentication or encryption options. For the backhaul credentials, +per-device PSK is not supported. + +If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to +specify the backhaul credentials, since the backhaul and fronthaul credentials +are identical. + +To enable the Multi-AP backhaul STA feature when it performs WPS, a new +parameter has been introduced to the WPS_PBC control interface call. When this +"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the +Association Request frame and the M1 message. It then configures the new network +profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does +not follow the Multi-AP specification, wpa_supplicant will fail to associate. + +In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e., +multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2 +and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or +multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS +in hostapd on Registrar AP). + +1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP + specific). +2. Enrollee sends Authentication frame (nothing Multi-AP specific). +3. AP sends Authentication frame (nothing Multi-AP specific). +4. Enrollee sends Association Request frame with Multi-AP IE. +5. AP sends Association Response frame with Multi-AP IE. +6. Enrollee sends M1 with additional Multi-AP subelement. +7. AP sends M8 with backhaul instead of fronthaul credentials. +8. Enrollee sends Deauthentication frame. + + +References +---------- + +[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh +[2] https://github.com/prplfoundation/prplMesh +[3] https://www.wi-fi.org/file/multi-ap-specification-v10 + (requires registration) diff --git a/hostapd/android.config b/hostapd/android.config index 08d21f044aa7..e14423f268cc 100644 --- a/hostapd/android.config +++ b/hostapd/android.config @@ -50,6 +50,9 @@ CONFIG_DRIVER_NL80211_QCA=y # Driver support is also needed for IEEE 802.11w. CONFIG_IEEE80211W=y +# Support Operating Channel Validation +#CONFIG_OCV=y + # Integrated EAP server #CONFIG_EAP=y diff --git a/hostapd/config_file.c b/hostapd/config_file.c index b26da71a8410..42f3b40ef48b 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -37,7 +37,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, const char *fname) { FILE *f; - char buf[128], *pos, *pos2; + char buf[128], *pos, *pos2, *pos3; int line = 0, vlan_id; struct hostapd_vlan *vlan; @@ -82,7 +82,10 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, pos2 = pos; while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') pos2++; - *pos2 = '\0'; + + if (*pos2 != '\0') + *(pos2++) = '\0'; + if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " "in '%s'", line, fname); @@ -90,6 +93,13 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, return -1; } + while (*pos2 == ' ' || *pos2 == '\t') + pos2++; + pos3 = pos2; + while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0') + pos3++; + *pos3 = '\0'; + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while reading " @@ -102,6 +112,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, vlan->vlan_desc.untagged = vlan_id; vlan->vlan_desc.notempty = !!vlan_id; os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); + os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge)); vlan->next = bss->vlan; bss->vlan = vlan; } @@ -1368,6 +1379,30 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf, #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + +static u8 find_bit_offset(u8 val) +{ + u8 res = 0; + + for (; val; val >>= 1) { + if (val & 1) + break; + res++; + } + + return res; +} + + +static u8 set_he_cap(int val, u8 mask) +{ + return (u8) (mask & (val << find_bit_offset(mask))); +} + +#endif /* CONFIG_IEEE80211AX */ + + #ifdef CONFIG_INTERWORKING static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, int line) @@ -2254,10 +2289,16 @@ static unsigned int parse_tls_flags(const char *val) flags |= TLS_CONN_DISABLE_TIME_CHECKS; if (os_strstr(val, "[DISABLE-TLSv1.0]")) flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(val, "[ENABLE-TLSv1.0]")) + flags |= TLS_CONN_ENABLE_TLSv1_0; if (os_strstr(val, "[DISABLE-TLSv1.1]")) flags |= TLS_CONN_DISABLE_TLSv1_1; + if (os_strstr(val, "[ENABLE-TLSv1.1]")) + flags |= TLS_CONN_ENABLE_TLSv1_1; if (os_strstr(val, "[DISABLE-TLSv1.2]")) flags |= TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(val, "[ENABLE-TLSv1.2]")) + flags |= TLS_CONN_ENABLE_TLSv1_2; if (os_strstr(val, "[DISABLE-TLSv1.3]")) flags |= TLS_CONN_DISABLE_TLSv1_3; if (os_strstr(val, "[ENABLE-TLSv1.3]")) @@ -2292,6 +2333,14 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) pos = pos2 + ETH_ALEN * 3 - 1; } + pos2 = os_strstr(pos, "|vlanid="); + if (pos2) { + if (!end) + end = pos2; + pos2 += 8; + pw->vlan_id = atoi(pos2); + } + pos2 = os_strstr(pos, "|id="); if (pos2) { if (!end) @@ -2476,8 +2525,22 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "private_key_passwd") == 0) { os_free(bss->private_key_passwd); bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "check_cert_subject") == 0) { + if (!pos[0]) { + wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", + line, pos); + return 1; + } + os_free(bss->check_cert_subject); + bss->check_cert_subject = os_strdup(pos); + if (!bss->check_cert_subject) + return 1; } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "check_crl_strict") == 0) { + bss->check_crl_strict = atoi(pos); + } else if (os_strcmp(buf, "crl_reload_interval") == 0) { + bss->crl_reload_interval = atoi(pos); } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { bss->tls_session_lifetime = atoi(pos); } else if (os_strcmp(buf, "tls_flags") == 0) { @@ -2494,6 +2557,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "openssl_ciphers") == 0) { os_free(bss->openssl_ciphers); bss->openssl_ciphers = os_strdup(pos); + } else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) { + os_free(bss->openssl_ecdh_curves); + bss->openssl_ecdh_curves = os_strdup(pos); } else if (os_strcmp(buf, "fragment_size") == 0) { bss->fragment_size = atoi(pos); #ifdef EAP_SERVER_FAST @@ -3070,9 +3136,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, * cause problems with the current implementation. * Since it is unlikely that this small numbers are * useful in real life scenarios, do not allow beacon - * period to be set below 15 TU. */ - if (val < 15 || val > 65535) { - wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)", + * period to be set below 10 TU. */ + if (val < 10 || val > 65535) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_int %d (expected 10..65535)", line, val); return 1; } @@ -3148,7 +3215,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, val); return 1; } - conf->send_probe_response = val; + bss->send_probe_response = val; } else if (os_strcmp(buf, "supported_rates") == 0) { if (hostapd_parse_intlist(&conf->supported_rates, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid rate list", @@ -3316,6 +3383,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + } else if (os_strcmp(buf, "ocv") == 0) { + bss->ocv = atoi(pos); + if (bss->ocv && !bss->ieee80211w) + bss->ieee80211w = 1; +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211N } else if (os_strcmp(buf, "ieee80211n") == 0) { conf->ieee80211n = atoi(pos); @@ -3369,6 +3442,90 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->he_op.he_twt_required = atoi(pos); } else if (os_strcmp(buf, "he_rts_threshold") == 0) { conf->he_op.he_rts_threshold = atoi(pos); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; #endif /* CONFIG_IEEE80211AX */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); @@ -3466,6 +3623,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, } } else if (os_strcmp(buf, "wps_cred_processing") == 0) { bss->wps_cred_processing = atoi(pos); + } else if (os_strcmp(buf, "wps_cred_add_sae") == 0) { + bss->wps_cred_add_sae = atoi(pos); } else if (os_strcmp(buf, "ap_settings") == 0) { os_free(bss->ap_settings); bss->ap_settings = @@ -3475,6 +3634,56 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + + if (!str || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen); + bss->multi_ap_backhaul_ssid.ssid_len = slen; + bss->multi_ap_backhaul_ssid.ssid_set = 1; + os_free(str); + } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) { + int len = os_strlen(pos); + + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WPA passphrase length %d (expected 8..63)", + line, len); + return 1; + } + os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); + bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos); + if (bss->multi_ap_backhaul_ssid.wpa_passphrase) { + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1; + } + } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) { + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + bss->multi_ap_backhaul_ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (!bss->multi_ap_backhaul_ssid.wpa_psk) + return 1; + if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN) || + pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, pos); + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + return 1; + } + bss->multi_ap_backhaul_ssid.wpa_psk->group = 1; + os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); + bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL; + bss->multi_ap_backhaul_ssid.wpa_psk_set = 1; } else if (os_strcmp(buf, "upnp_iface") == 0) { os_free(bss->upnp_iface); bss->upnp_iface = os_strdup(pos); @@ -3717,6 +3926,16 @@ static int hostapd_config_fill(struct hostapd_config *conf, #ifdef CONFIG_HS20 } else if (os_strcmp(buf, "hs20") == 0) { bss->hs20 = atoi(pos); + } else if (os_strcmp(buf, "hs20_release") == 0) { + int val = atoi(pos); + + if (val < 1 || val > (HS20_VERSION >> 4) + 1) { + wpa_printf(MSG_ERROR, + "Line %d: Unsupported hs20_release: %s", + line, pos); + return 1; + } + bss->hs20_release = val; } else if (os_strcmp(buf, "disable_dgaf") == 0) { bss->disable_dgaf = atoi(pos); } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { @@ -3807,6 +4026,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { os_free(bss->t_c_server_url); bss->t_c_server_url = os_strdup(pos); + } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) { + os_free(bss->hs20_sim_provisioning_url); + bss->hs20_sim_provisioning_url = os_strdup(pos); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO } else if (os_strcmp(buf, "mbo") == 0) { @@ -4111,6 +4333,22 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) { bss->coloc_intf_reporting = atoi(pos); #endif /* CONFIG_OWE */ + } else if (os_strcmp(buf, "multi_ap") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 3) { + wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'", + line, buf); + return -1; + } + + bss->multi_ap = val; + } else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) { + conf->rssi_reject_assoc_rssi = atoi(pos); + } else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) { + conf->rssi_reject_assoc_timeout = atoi(pos); + } else if (os_strcmp(buf, "pbss") == 0) { + bss->pbss = atoi(pos); } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 75f12e543f21..e4b16e61af0e 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -883,7 +883,7 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, /* TODO: TSF configurable/learnable */ bss_term_dur[0] = 4; /* Subelement ID */ bss_term_dur[1] = 10; /* Length */ - os_memset(bss_term_dur, 2, 8); + os_memset(&bss_term_dur[2], 0, 8); end = os_strchr(pos, ','); if (end == NULL) { wpa_printf(MSG_DEBUG, "Invalid bss_term data"); @@ -1488,6 +1488,63 @@ static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface) } +static int +hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + struct hostapd_wpa_psk *psk; + const u8 *pmk; + int pmk_len; + int pmk_match; + int sta_match; + int bss_match; + int reason; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + + for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) { + pmk_match = PMK_LEN == pmk_len && + os_memcmp(psk->psk, pmk, pmk_len) == 0; + sta_match = psk->group == 0 && + os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0; + bss_match = psk->group == 1; + + if (pmk_match && (sta_match || bss_match)) + return 0; + } + + wpa_printf(MSG_INFO, "STA " MACSTR + " PSK/passphrase no longer valid - disconnect", + MAC2STR(sta->addr)); + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + hostapd_drv_sta_deauth(hapd, sta->addr, reason); + ap_sta_deauthenticate(hapd, sta, reason); + + return 0; +} + + +static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + int err; + + hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk); + + err = hostapd_setup_wpa_psk(conf); + if (err < 0) { + wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d", + err); + return -1; + } + + ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter, + NULL); + + return 0; +} + + #ifdef CONFIG_TESTING_OPTIONS static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) @@ -2826,6 +2883,34 @@ static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num, } +static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd, + const char *field, char *buf, + size_t buflen) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field); + +#ifdef CONFIG_DPP + if (os_strcmp(field, "dpp") == 0) { + int res; + +#ifdef CONFIG_DPP2 + res = os_snprintf(buf, buflen, "DPP=2"); +#else /* CONFIG_DPP2 */ + res = os_snprintf(buf, buflen, "DPP=1"); +#endif /* CONFIG_DPP2 */ + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_DPP */ + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; +} + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, @@ -3013,6 +3098,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "ENABLE", 6) == 0) { if (hostapd_ctrl_iface_enable(hapd->iface)) reply_len = -1; + } else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) { + if (hostapd_ctrl_iface_reload_wpa_psk(hapd)) + reply_len = -1; } else if (os_strncmp(buf, "RELOAD", 6) == 0) { if (hostapd_ctrl_iface_reload(hapd->iface)) reply_len = -1; @@ -3182,7 +3270,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { - res = hostapd_dpp_bootstrap_gen(hapd, buf + 18); + res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18); if (res < 0) { reply_len = -1; } else { @@ -3191,12 +3279,14 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { - if (hostapd_dpp_bootstrap_remove(hapd, buf + 21) < 0) + if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp, + buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { const char *uri; - uri = hostapd_dpp_bootstrap_get_uri(hapd, atoi(buf + 22)); + uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp, + atoi(buf + 22)); if (!uri) { reply_len = -1; } else { @@ -3205,8 +3295,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { - reply_len = hostapd_dpp_bootstrap_info(hapd, atoi(buf + 19), - reply, reply_size); + reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp, + atoi(buf + 19), + reply, reply_size); } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { if (hostapd_dpp_auth_init(hapd, buf + 13) < 0) reply_len = -1; @@ -3217,7 +3308,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, hostapd_dpp_stop(hapd); hostapd_dpp_listen_stop(hapd); } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { - res = hostapd_dpp_configurator_add(hapd, buf + 20); + res = dpp_configurator_add(hapd->iface->interfaces->dpp, + buf + 20); if (res < 0) { reply_len = -1; } else { @@ -3226,15 +3318,17 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { - if (hostapd_dpp_configurator_remove(hapd, buf + 24) < 0) + if (dpp_configurator_remove(hapd->iface->interfaces->dpp, + buf + 24) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { - if (hostapd_dpp_configurator_sign(hapd, buf + 22) < 0) + if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { - reply_len = hostapd_dpp_configurator_get_key(hapd, - atoi(buf + 25), - reply, reply_size); + reply_len = dpp_configurator_get_key_id( + hapd->iface->interfaces->dpp, + atoi(buf + 25), + reply, reply_size); } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { res = hostapd_dpp_pkex_add(hapd, buf + 12); if (res < 0) { @@ -3253,6 +3347,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0) reply_len = -1; #endif /* RADIUS_SERVER */ + } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { + reply_len = hostapd_ctrl_iface_get_capability( + hapd, buf + 15, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -3506,18 +3603,18 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (hapd->conf->ctrl_interface_gid_set && - chown(hapd->conf->ctrl_interface, -1, - hapd->conf->ctrl_interface_gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(hapd->conf->ctrl_interface, -1, + hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); return -1; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && - chown(hapd->conf->ctrl_interface, -1, - hapd->iface->interfaces->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(hapd->conf->ctrl_interface, -1, + hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); return -1; } @@ -3590,16 +3687,16 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (hapd->conf->ctrl_interface_gid_set && - chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", strerror(errno)); goto fail; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && - chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", strerror(errno)); goto fail; } @@ -3733,7 +3830,7 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_DPP - hostapd_dpp_deinit_global(interfaces); + dpp_global_clear(interfaces->dpp); #endif /* CONFIG_DPP */ } @@ -4273,9 +4370,9 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) goto fail; } } else if (interface->ctrl_iface_group && - chown(interface->global_iface_path, -1, - interface->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(interface->global_iface_path, -1, + interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); goto fail; } @@ -4332,8 +4429,8 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) } if (interface->ctrl_iface_group && - chown(fname, -1, interface->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(fname, -1, interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); goto fail; } diff --git a/hostapd/defconfig b/hostapd/defconfig index 77a894d5e11a..ea5e2c9de04f 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -53,6 +53,9 @@ CONFIG_RSN_PREAUTH=y # IEEE 802.11w (management frame protection) CONFIG_IEEE80211W=y +# Support Operating Channel Validation +#CONFIG_OCV=y + # Integrated EAP server CONFIG_EAP=y @@ -249,6 +252,11 @@ CONFIG_IPV6=y # requirements described above. #CONFIG_NO_RANDOM_POOL=y +# Should we attempt to use the getrandom(2) call that provides more reliable +# yet secure randomness source than /dev/random on Linux 3.17 and newer. +# Requires glibc 2.25 to build, falls back to /dev/random if unavailable. +#CONFIG_GETRANDOM=y + # Should we use poll instead of select? Select is used by default. #CONFIG_ELOOP_POLL=y @@ -356,8 +364,6 @@ CONFIG_IPV6=y #CONFIG_TAXONOMY=y # Fast Initial Link Setup (FILS) (IEEE 802.11ai) -# Note: This is an experimental and not yet complete implementation. This -# should not be enabled for production use. #CONFIG_FILS=y # FILS shared key authentication with PFS #CONFIG_FILS_SK_PFS=y diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index a005217116f1..f8caa56239d3 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -438,6 +438,13 @@ wmm_ac_vo_txop_limit=47 wmm_ac_vo_acm=0 # Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 +# Enable Multi-AP functionality +# 0 = disabled (default) +# 1 = AP support backhaul BSS +# 2 = AP support fronthaul BSS +# 3 = AP supports both backhaul BSS and fronthaul BSS +#multi_ap=0 + # Static WEP key configuration # # The key number to use when transmitting. @@ -794,6 +801,30 @@ wmm_ac_vo_acm=0 # unsigned integer = duration in units of 16 us #he_rts_threshold=0 +#he_mu_edca_qos_info_param_count +#he_mu_edca_qos_info_q_ack +#he_mu_edca_qos_info_queue_request=1 +#he_mu_edca_qos_info_txop_request +#he_mu_edca_ac_be_aifsn=0 +#he_mu_edca_ac_be_ecwmin=15 +#he_mu_edca_ac_be_ecwmax=15 +#he_mu_edca_ac_be_timer=255 +#he_mu_edca_ac_bk_aifsn=0 +#he_mu_edca_ac_bk_aci=1 +#he_mu_edca_ac_bk_ecwmin=15 +#he_mu_edca_ac_bk_ecwmax=15 +#he_mu_edca_ac_bk_timer=255 +#he_mu_edca_ac_vi_ecwmin=15 +#he_mu_edca_ac_vi_ecwmax=15 +#he_mu_edca_ac_vi_aifsn=0 +#he_mu_edca_ac_vi_aci=2 +#he_mu_edca_ac_vi_timer=255 +#he_mu_edca_ac_vo_aifsn=0 +#he_mu_edca_ac_vo_aci=3 +#he_mu_edca_ac_vo_ecwmin=15 +#he_mu_edca_ac_vo_ecwmax=15 +#he_mu_edca_ac_vo_timer=255 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -891,18 +922,83 @@ eap_server=0 # valid CRL signed by the CA is required to be included in the ca_cert file. # This can be done by using PEM format for CA certificate and CRL and # concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. +# restarted to take the new CRL into use. Alternatively, crl_reload_interval can +# be used to configure periodic updating of the loaded CRL information. # 0 = do not verify CRLs (default) # 1 = check the CRL of the user certificate # 2 = check all CRLs in the certificate path #check_crl=1 +# Specify whether to ignore certificate CRL validity time mismatches with +# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID. +# +# 0 = ignore errors +# 1 = do not ignore errors (default) +#check_crl_strict=1 + +# CRL reload interval in seconds +# This can be used to reload ca_cert file and the included CRL on every new TLS +# session if difference between last reload and the current reload time in +# seconds is greater than crl_reload_interval. +# Note: If interval time is very short, CPU overhead may be negatively affected +# and it is advised to not go below 300 seconds. +# This is applicable only with check_crl values 1 and 2. +# 0 = do not reload CRLs (default) +# crl_reload_interval = 300 + +# If check_cert_subject is set, the value of every field will be checked +# against the DN of the subject in the client certificate. If the values do +# not match, the certificate verification will fail, rejecting the user. +# This option allows hostapd to match every individual field in the right order +# against the DN of the subject in the client certificate. +# +# For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will check +# every individual DN field of the subject in the client certificate. If OU=XYZ +# comes first in terms of the order in the client certificate (DN field of +# client certificate C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), hostapd will reject the +# client because the order of 'OU' is not matching the specified string in +# check_cert_subject. +# +# This option also allows '*' as a wildcard. This option has some limitation. +# It can only be used as per the following example. +# +# For example, check_cert_subject=C=US/O=XX/OU=Production* and we have two +# clients and DN of the subject in the first client certificate is +# (C=US/O=XX/OU=Production Unit) and DN of the subject in the second client is +# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both +# clients because the value of 'OU' field in both client certificates matches +# 'OU' value in 'check_cert_subject' up to 'wildcard'. +# +# * (Allow all clients, e.g., check_cert_subject=*) +#check_cert_subject=string + # TLS Session Lifetime in seconds # This can be used to allow TLS sessions to be cached and resumed with an # abbreviated handshake when using EAP-TLS/TTLS/PEAP. # (default: 0 = session caching and resumption disabled) #tls_session_lifetime=3600 +# TLS flags +# [ALLOW-SIGN-RSA-MD5] = allow MD5-based certificate signatures (depending on +# the TLS library, these may be disabled by default to enforce stronger +# security) +# [DISABLE-TIME-CHECKS] = ignore certificate validity time (this requests +# the TLS library to accept certificates even if they are not currently +# valid, i.e., have expired or have not yet become valid; this should be +# used only for testing purposes) +# [DISABLE-TLSv1.0] = disable use of TLSv1.0 +# [ENABLE-TLSv1.0] = explicitly enable use of TLSv1.0 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.1] = disable use of TLSv1.1 +# [ENABLE-TLSv1.1] = explicitly enable use of TLSv1.1 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.2] = disable use of TLSv1.2 +# [ENABLE-TLSv1.2] = explicitly enable use of TLSv1.2 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.3] = disable use of TLSv1.3 +# [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default) +#tls_flags=[flag1][flag2]... + # Cached OCSP stapling response (DER encoded) # If set, this file is sent as a certificate status response by the EAP server # if the EAP peer requests certificate status in the ClientHello message. @@ -944,6 +1040,19 @@ eap_server=0 # use OpenSSL. #openssl_ciphers=DEFAULT:!EXP:!LOW +# OpenSSL ECDH curves +# +# This is an OpenSSL specific configuration option for configuring the ECDH +# curves for EAP-TLS/TTLS/PEAP/FAST server. If not set, automatic curve +# selection is enabled. If set to an empty string, ECDH curve configuration is +# not done (the exact library behavior depends on the library version). +# Otherwise, this is a colon separated list of the supported curves (e.g., +# P-521:P-384:P-256). This is applicable only if hostapd is built to use +# OpenSSL. This must not be used for Suite B cases since the same OpenSSL +# parameter is set differently in those cases and this might conflict with that +# design. +#openssl_ecdh_curves=P-521:P-384:P-256 + # Fragment size for EAP methods #fragment_size=1400 @@ -1104,8 +1213,10 @@ own_ip_addr=127.0.0.1 # Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value # VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can # be used to set static client MAC address to VLAN ID mapping. -# 0 = disabled (default) -# 1 = option; use default interface if RADIUS server does not include VLAN ID +# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2 +# passphrase from wpa_psk_file or vlan_id parameter from sae_password. +# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used +# 1 = optional; use default interface if RADIUS server does not include VLAN ID # 2 = required; reject authentication if RADIUS server does not include VLAN ID #dynamic_vlan=0 @@ -1128,6 +1239,7 @@ own_ip_addr=127.0.0.1 # white space (space or tab). # If no entries are provided by this file, the station is statically mapped # to . interfaces. +# Each line can optionally also contain the name of a bridge to add the VLAN to #vlan_file=/etc/hostapd.vlan # Interface where 802.1q tagged packets should appear when a RADIUS server is @@ -1418,6 +1530,13 @@ own_ip_addr=127.0.0.1 # dot11AssociationSAQueryRetryTimeout, 1...4294967295 #assoc_sa_query_retry_timeout=201 +# ocv: Operating Channel Validation +# This is a countermeasure against multi-channel man-in-the-middle attacks. +# Enabling this automatically also enables ieee80211w, if not yet enabled. +# 0 = disabled (default) +# 1 = enabled +#ocv=1 + # disable_pmksa_caching: Disable PMKSA caching # This parameter can be used to disable caching of PMKSA created through EAP # authentication. RSN preauthentication may still end up using PMKSA caching if @@ -1445,21 +1564,29 @@ own_ip_addr=127.0.0.1 # corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value # starts with the password (dot11RSNAConfigPasswordCredential). That value can # be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and -# by optional password identifier (dot11RSNAConfigPasswordIdentifier). If the -# peer MAC address is not included or is set to the wildcard address +# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In +# addition, an optional VLAN ID specification can be used to bind the station +# to the specified VLAN whenver the specific SAE password entry is used. +# +# If the peer MAC address is not included or is set to the wildcard address # (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a # specific peer MAC address is included, only a station with that MAC address -# is allowed to use the entry. If the password identifier (with non-zero length) -# is included, the entry is limited to be used only with that specified -# identifier. The last matching (based on peer MAC address and identifier) entry -# is used to select which password to use. Setting sae_password to an empty -# string has a special meaning of removing all previously added entries. +# is allowed to use the entry. +# +# If the password identifier (with non-zero length) is included, the entry is +# limited to be used only with that specified identifier. + +# The last matching (based on peer MAC address and identifier) entry is used to +# select which password to use. Setting sae_password to an empty string has a +# special meaning of removing all previously added entries. +# # sae_password uses the following encoding: -#[|mac=][|id=] +#[|mac=][|vlanid=][|id=] # Examples: #sae_password=secret #sae_password=really secret|mac=ff:ff:ff:ff:ff:ff #sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier +#sae_password=example secret|vlanid=3|id=pw identifier # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold) # This parameter defines how many open SAE instances can be in progress at the @@ -1473,12 +1600,16 @@ own_ip_addr=127.0.0.1 # Enabled SAE finite cyclic groups # SAE implementation are required to support group 19 (ECC group defined over a -# 256-bit prime order field). All groups that are supported by the -# implementation are enabled by default. This configuration parameter can be -# used to specify a limited set of allowed groups. The group values are listed -# in the IANA registry: +# 256-bit prime order field). This configuration parameter can be used to +# specify a set of allowed groups. If not included, only the mandatory group 19 +# is enabled. +# The group values are listed in the IANA registry: # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 -#sae_groups=19 20 21 25 26 +# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production +# purposes due limited security (see RFC 8247). Groups that are not as strong as +# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases +# since all implementations are required to support group 19. +#sae_groups=19 20 21 # Require MFP for all associations using SAE # This parameter can be used to enforce negotiation of MFP for all associations @@ -1837,6 +1968,14 @@ own_ip_addr=127.0.0.1 # the configuration appropriately in this case. #wps_cred_processing=0 +# Whether to enable SAE (WPA3-Personal transition mode) automatically for +# WPA2-PSK credentials received using WPS. +# 0 = only add the explicitly listed WPA2-PSK configuration (default) +# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the +# AP gets configured in WPA3-Personal transition mode (supports both +# WPA2-Personal (PSK) and WPA3-Personal (SAE) clients). +#wps_cred_add_sae=0 + # AP Settings Attributes for M7 # By default, hostapd generates the AP Settings Attributes for M7 based on the # current configuration. It is possible to override this by providing a file @@ -1845,6 +1984,15 @@ own_ip_addr=127.0.0.1 # attribute. #ap_settings=hostapd.ap_settings +# Multi-AP backhaul BSS config +# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials. +# These are passed in WPS M8 instead of the normal (fronthaul) credentials +# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted +# like ssid2. The key is set like wpa_psk or wpa_passphrase. +#multi_ap_backhaul_ssid="backhaul" +#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#multi_ap_backhaul_wpa_passphrase=secret passphrase + # WPS UPnP interface # If set, support for external Registrars is enabled. #upnp_iface=br0 @@ -2273,6 +2421,21 @@ own_ip_addr=127.0.0.1 # Default is 0 = OCE disabled #oce=0 +# RSSI-based assocition rejection +# +# Reject STA association if RSSI is below given threshold (in dBm) +# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled) +# Note: This rejection happens based on a signal strength detected while +# receiving a single frame and as such, there is significant risk of the value +# not being accurate and this resulting in valid stations being rejected. As +# such, this functionality is not recommended to be used for purposes other than +# testing. +#rssi_reject_assoc_rssi=-75 +# +# Association retry delay in seconds allowed by the STA if RSSI has not met the +# threshold (range: 0..255, default=30). +#rssi_reject_assoc_timeout=30 + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration diff --git a/hostapd/hostapd.wpa_psk b/hostapd/hostapd.wpa_psk index 0a9499acd736..166e59e9c64f 100644 --- a/hostapd/hostapd.wpa_psk +++ b/hostapd/hostapd.wpa_psk @@ -3,7 +3,13 @@ # Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that # anyone can use. PSK can be configured as an ASCII passphrase of 8..63 # characters or as a 256-bit hex PSK (64 hex digits). +# An optional key identifier can be added by prefixing the line with +# keyid= +# An optional VLAN ID can be specified by prefixing the line with +# vlanid=. 00:00:00:00:00:00 secret passphrase 00:11:22:33:44:55 another passphrase 00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +keyid=example_id 00:11:22:33:44:77 passphrase with keyid +vlanid=3 00:00:00:00:00:00 passphrase with vlanid 00:00:00:00:00:00 another passphrase for all STAs diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 489da397c63d..23c592a6b0a6 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1,6 +1,6 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2018, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,7 +21,7 @@ static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2018, Jouni Malinen and contributors"; +"Copyright (c) 2004-2019, Jouni Malinen and contributors"; static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; @@ -1443,6 +1443,13 @@ static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, } +static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv); +} + + static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1480,6 +1487,20 @@ static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv); +} + + +static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK"); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1640,6 +1661,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key, NULL, " = Get DPP configurator's private key" }, + { "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL, + "conf= configurator= = generate self DPP configuration" }, { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL, "add PKEX code" }, { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL, @@ -1651,6 +1674,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { "=Add/Delete/Show/Clear deny MAC ACL" }, { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations, " = poll a STA to check connectivity with a QoS null frame" }, + { "req_beacon", hostapd_cli_cmd_req_beacon, NULL, + " [req_mode=] = send a Beacon report request to a station" }, + { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, + "= reload wpa_psk_file only" }, { NULL, NULL, NULL, NULL } }; diff --git a/hostapd/main.c b/hostapd/main.c index 414dfe4241e0..93d2dd34c08e 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -1,6 +1,6 @@ /* * hostapd / main() - * Copyright (c) 2002-2018, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +18,7 @@ #include "crypto/random.h" #include "crypto/tls.h" #include "common/version.h" +#include "common/dpp.h" #include "drivers/driver.h" #include "eap_server/eap.h" #include "eap_server/tncs.h" @@ -456,7 +457,7 @@ static void show_version(void) "hostapd v" VERSION_STR "\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2018, Jouni Malinen " + "Copyright (c) 2002-2019, Jouni Malinen " "and contributors\n"); } @@ -671,7 +672,9 @@ int main(int argc, char *argv[]) dl_list_init(&interfaces.eth_p_oui); #endif /* CONFIG_ETH_P_OUI */ #ifdef CONFIG_DPP - hostapd_dpp_init_global(&interfaces); + interfaces.dpp = dpp_global_init(); + if (!interfaces.dpp) + return -1; #endif /* CONFIG_DPP */ for (;;) { @@ -901,7 +904,7 @@ int main(int argc, char *argv[]) os_free(interfaces.iface); #ifdef CONFIG_DPP - hostapd_dpp_deinit_global(&interfaces); + dpp_global_deinit(interfaces.dpp); #endif /* CONFIG_DPP */ if (interfaces.eloop_initialized) diff --git a/hostapd/wps-ap-nfc.py b/hostapd/wps-ap-nfc.py index 2fc301296e88..258d84148fe1 100755 --- a/hostapd/wps-ap-nfc.py +++ b/hostapd/wps-ap-nfc.py @@ -26,7 +26,7 @@ success_file = None def summary(txt): - print txt + print(txt) if summary_file: with open(summary_file, 'a') as f: f.write(txt + "\n") @@ -42,19 +42,19 @@ def wpas_connect(): if os.path.isdir(wpas_ctrl): try: ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] - except OSError, error: - print "Could not find hostapd: ", error + except OSError as error: + print("Could not find hostapd: ", error) return None if len(ifaces) < 1: - print "No hostapd control interface found" + print("No hostapd control interface found") return None for ctrl in ifaces: try: wpas = wpaspy.Ctrl(ctrl) return wpas - except Exception, e: + except Exception as e: pass return None @@ -133,23 +133,23 @@ def _process_request(self, request): def process_request(self, request): summary("HandoverServer - request received") try: - print "Parsed handover request: " + request.pretty() - except Exception, e: - print e - print str(request).encode("hex") + print("Parsed handover request: " + request.pretty()) + except Exception as e: + print(e) + print(str(request).encode("hex")) sel = nfc.ndef.HandoverSelectMessage(version="1.2") for carrier in request.carriers: - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.wsc": summary("WPS carrier type match - add WPS carrier record") data = wpas_get_handover_sel() if data is None: summary("Could not get handover select carrier record from hostapd") continue - print "Handover select carrier record from hostapd:" - print data.encode("hex") + print("Handover select carrier record from hostapd:") + print(data.encode("hex")) if "OK" in wpas_report_handover(carrier.record, data): success_report("Handover reported successfully") else: @@ -158,12 +158,12 @@ def process_request(self, request): message = nfc.ndef.Message(data); sel.add_carrier(message[0], "active", message[1:]) - print "Handover select:" + print("Handover select:") try: - print sel.pretty() - except Exception, e: - print e - print str(sel).encode("hex") + print(sel.pretty()) + except Exception as e: + print(e) + print(str(sel).encode("hex")) summary("Sending handover select") self.success = True @@ -174,7 +174,7 @@ def wps_tag_read(tag): success = False if len(tag.ndef.message): for record in tag.ndef.message: - print "record type " + record.type + print("record type " + record.type) if record.type == "application/vnd.wfa.wsc": summary("WPS tag - send to hostapd") success = wpas_tag_read(tag.ndef.message) @@ -193,7 +193,7 @@ def rdwr_connected_write(tag): global write_data tag.ndef.message = str(write_data) success_report("Tag write succeeded") - print "Done - remove tag" + print("Done - remove tag") global only_one if only_one: global continue_loop @@ -211,7 +211,7 @@ def wps_write_config_tag(clf, wait_remove=True): summary("Could not get WPS config token from hostapd") return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) @@ -224,7 +224,7 @@ def wps_write_password_tag(clf, wait_remove=True): summary("Could not get WPS password token from hostapd") return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) @@ -233,11 +233,11 @@ def rdwr_connected(tag): summary("Tag connected: " + str(tag)) if tag.ndef: - print "NDEF tag: " + tag.type + print("NDEF tag: " + tag.type) try: - print tag.ndef.message.pretty() - except Exception, e: - print e + print(tag.ndef.message.pretty()) + except Exception as e: + print(e) success = wps_tag_read(tag) if only_one and success: global continue_loop @@ -250,13 +250,13 @@ def rdwr_connected(tag): def llcp_startup(clf, llc): - print "Start LLCP server" + print("Start LLCP server") global srv srv = HandoverServer(llc) return llc def llcp_connected(llc): - print "P2P LLCP connected" + print("P2P LLCP connected") global wait_connection wait_connection = False global srv @@ -304,7 +304,7 @@ def main(): try: if not clf.open("usb"): - print "Could not open connection with an NFC device" + print("Could not open connection with an NFC device") raise SystemExit if args.command == "write-config": @@ -317,15 +317,15 @@ def main(): global continue_loop while continue_loop: - print "Waiting for a tag or peer to be touched" + print("Waiting for a tag or peer to be touched") wait_connection = True try: if not clf.connect(rdwr={'on-connect': rdwr_connected}, llcp={'on-startup': llcp_startup, 'on-connect': llcp_connected}): break - except Exception, e: - print "clf.connect failed" + except Exception as e: + print("clf.connect failed") global srv if only_one and srv and srv.success: diff --git a/hs20/client/.gitignore b/hs20/client/.gitignore new file mode 100644 index 000000000000..d2fd60fb4441 --- /dev/null +++ b/hs20/client/.gitignore @@ -0,0 +1 @@ +hs20-osu-client diff --git a/hs20/client/Makefile b/hs20/client/Makefile index fc9b61940c4f..67f6f55c5260 100644 --- a/hs20/client/Makefile +++ b/hs20/client/Makefile @@ -8,12 +8,17 @@ ifndef LDO LDO=$(CC) endif +ifeq ($(QUIET), 1) +Q=@ +E=true +else Q=@ E=echo ifeq ($(V), 1) Q= E=true endif +endif ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g diff --git a/hs20/client/est.c b/hs20/client/est.c index b1aacb8ffbbe..db65334b20f3 100644 --- a/hs20/client/est.c +++ b/hs20/client/est.c @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef OPENSSL_IS_BORINGSSL #include #endif /* OPENSSL_IS_BORINGSSL */ @@ -219,6 +220,10 @@ typedef struct { } d; } AttrOrOID; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) +DEFINE_STACK_OF(AttrOrOID) +#endif + typedef struct { int type; STACK_OF(AttrOrOID) *attrs; @@ -352,9 +357,17 @@ static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs, } } #else /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) + num = sk_AttrOrOID_num(csrattrs->attrs); +#else num = SKM_sk_num(AttrOrOID, csrattrs->attrs); +#endif for (i = 0; i < num; i++) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL) + AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i); +#else AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i); +#endif switch (ao->type) { case 0: add_csrattrs_oid(ctx, ao->d.oid, exts); diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c index 636e10666f8b..1f594ce8a25a 100644 --- a/hs20/client/osu_client.c +++ b/hs20/client/osu_client.c @@ -117,8 +117,8 @@ static int android_update_permission(const char *path, mode_t mode) /* Allow processes running with Group ID as AID_WIFI, * to read files from SP, SP/, Cert and osu-info directories */ - if (chown(path, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s", + if (lchown(path, -1, AID_WIFI)) { + wpa_printf(MSG_INFO, "CTRL: Could not lchown directory: %s", strerror(errno)); return -1; } @@ -612,8 +612,8 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, } } - android_update_permission("SP", S_IRWXU | S_IRGRP | S_IXGRP); - android_update_permission(fname, S_IRWXU | S_IRGRP | S_IXGRP); + android_update_permission("SP", S_IRWXU | S_IRWXG); + android_update_permission(fname, S_IRWXU | S_IRWXG); snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); diff --git a/src/ap/acs.c b/src/ap/acs.c index 6d4c0416dd42..3b4507575328 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -13,6 +13,7 @@ #include "utils/common.h" #include "utils/list.h" #include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" #include "common/wpa_ctrl.h" #include "drivers/driver.h" #include "hostapd.h" @@ -362,7 +363,7 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface, } -static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) +static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan) { const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; @@ -376,7 +377,7 @@ static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) } -static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) +static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan) { const int allowed[] = { 36, 52, 100, 116, 132, 149 }; unsigned int i; @@ -389,6 +390,19 @@ static int acs_usable_vht80_chan(struct hostapd_channel_data *chan) } +static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 100 }; + 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)) { @@ -565,6 +579,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) long double factor, ideal_factor = 0; int i, j; int n_chans = 1; + u32 bw; unsigned int k; /* TODO: HT40- support */ @@ -579,16 +594,23 @@ acs_find_ideal_chan(struct hostapd_iface *iface) iface->conf->secondary_channel) n_chans = 2; - if (iface->conf->ieee80211ac && - iface->conf->vht_oper_chwidth == 1) - n_chans = 4; + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + } + } - /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ + bw = num_chan_to_bw(n_chans); - wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", - n_chans == 1 ? 20 : - n_chans == 2 ? 40 : - 80); + /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */ + + wpa_printf(MSG_DEBUG, + "ACS: Survey analysis for selected bandwidth %d MHz", bw); for (i = 0; i < iface->current_mode->num_channels; i++) { double total_weight; @@ -596,12 +618,23 @@ acs_find_ideal_chan(struct hostapd_iface *iface) chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) + /* Since in the current ACS implementation the first channel is + * always a primary channel, skip channels not available as + * primary until more sophisticated channel selection is + * implemented. */ + if (!chan_pri_allowed(chan)) continue; if (!is_in_chanlist(iface, chan)) continue; + if (!chan_bw_allowed(chan, bw, 1, 1)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: BW %u is not supported", + chan->chan, bw); + 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 && @@ -614,12 +647,24 @@ acs_find_ideal_chan(struct hostapd_iface *iface) } 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; + iface->conf->ieee80211ac) { + if (iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_80MHZ && + !acs_usable_vht80_chan(chan)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for VHT80", + chan->chan); + continue; + } + + if (iface->conf->vht_oper_chwidth == + VHT_CHANWIDTH_160MHZ && + !acs_usable_vht160_chan(chan)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: not allowed as primary channel for VHT160", + chan->chan); + continue; + } } factor = 0; @@ -632,6 +677,13 @@ acs_find_ideal_chan(struct hostapd_iface *iface) if (!adj_chan) break; + if (!chan_bw_allowed(adj_chan, bw, 1, 0)) { + wpa_printf(MSG_DEBUG, + "ACS: PRI Channel %d: secondary channel %d BW %u is not supported", + chan->chan, adj_chan->chan, bw); + break; + } + if (acs_usable_chan(adj_chan)) { factor += adj_chan->interference_factor; total_weight += 1; @@ -744,10 +796,14 @@ static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) case VHT_CHANWIDTH_80MHZ: offset = 6; break; + case VHT_CHANWIDTH_160MHZ: + offset = 14; + 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"); + wpa_printf(MSG_INFO, + "ACS: Only VHT20/40/80/160 is supported now"); return; } diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index f9b6f295927f..e640e9984b70 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -131,6 +131,15 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) * This can be enabled by default once the implementation has been fully * completed and tested with other implementations. */ bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3; + + bss->send_probe_response = 1; + +#ifdef CONFIG_HS20 + bss->hs20_release = (HS20_VERSION >> 4) + 1; +#endif /* CONFIG_HS20 */ + + /* Default to strict CRL checking. */ + bss->check_crl_strict = 1; } @@ -191,9 +200,8 @@ struct hostapd_config * hostapd_config_defaults(void) conf->num_bss = 1; 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; + conf->rts_threshold = -2; /* use driver default: 2347 */ + conf->fragm_threshold = -2; /* user driver default: 2346 */ /* Set to invalid value means do not add Power Constraint IE */ conf->local_pwr_constraint = -1; @@ -233,6 +241,9 @@ struct hostapd_config * hostapd_config_defaults(void) * environments for the current frequency band in the country. */ conf->country[2] = ' '; + conf->rssi_reject_assoc_rssi = 0; + conf->rssi_reject_assoc_timeout = 30; + return conf; } @@ -248,6 +259,12 @@ static int hostapd_config_read_wpa_psk(const char *fname, { FILE *f; char buf[128], *pos; + const char *keyid; + char *context; + char *context2; + char *token; + char *name; + char *value; int line = 0, ret = 0, len, ok; u8 addr[ETH_ALEN]; struct hostapd_wpa_psk *psk; @@ -262,6 +279,8 @@ static int hostapd_config_read_wpa_psk(const char *fname, } while (fgets(buf, sizeof(buf), f)) { + int vlan_id = 0; + line++; if (buf[0] == '#') @@ -277,9 +296,39 @@ static int hostapd_config_read_wpa_psk(const char *fname, if (buf[0] == '\0') continue; - if (hwaddr_aton(buf, addr)) { + context = NULL; + keyid = NULL; + while ((token = str_token(buf, " ", &context))) { + if (!os_strchr(token, '=')) + break; + context2 = NULL; + name = str_token(token, "=", &context2); + if (!name) + break; + value = str_token(token, "", &context2); + if (!value) + value = ""; + if (!os_strcmp(name, "keyid")) { + keyid = value; + } else if (!os_strcmp(name, "vlanid")) { + vlan_id = atoi(value); + } else { + wpa_printf(MSG_ERROR, + "Unrecognized '%s=%s' on line %d in '%s'", + name, value, line, fname); + ret = -1; + break; + } + } + + if (ret == -1) + break; + + if (!token) + token = ""; + if (hwaddr_aton(token, addr)) { wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " - "line %d in '%s'", buf, line, fname); + "line %d in '%s'", token, line, fname); ret = -1; break; } @@ -290,20 +339,20 @@ static int hostapd_config_read_wpa_psk(const char *fname, ret = -1; break; } + psk->vlan_id = vlan_id; if (is_zero_ether_addr(addr)) psk->group = 1; else os_memcpy(psk->addr, addr, ETH_ALEN); - pos = buf + 17; - if (*pos == '\0') { + pos = str_token(buf, "", &context); + if (!pos) { wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", line, fname); os_free(psk); ret = -1; break; } - pos++; ok = 0; len = os_strlen(pos); @@ -322,6 +371,18 @@ static int hostapd_config_read_wpa_psk(const char *fname, break; } + if (keyid) { + len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid)); + if ((size_t) len >= sizeof(psk->keyid)) { + wpa_printf(MSG_ERROR, + "PSK keyid too long on line %d in '%s'", + line, fname); + os_free(psk); + ret = -1; + break; + } + } + psk->next = ssid->wpa_psk; ssid->wpa_psk = psk; } @@ -534,10 +595,12 @@ 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->check_cert_subject); os_free(conf->ocsp_stapling_response); os_free(conf->ocsp_stapling_response_multi); os_free(conf->dh_file); os_free(conf->openssl_ciphers); + os_free(conf->openssl_ecdh_curves); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); @@ -582,6 +645,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->ap_pin); os_free(conf->extra_cred); os_free(conf->ap_settings); + hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk); + str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase); os_free(conf->upnp_iface); os_free(conf->friendly_name); os_free(conf->manufacturer_url); @@ -644,6 +709,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->hs20_operator_icon); } os_free(conf->subscr_remediation_url); + os_free(conf->hs20_sim_provisioning_url); os_free(conf->t_c_filename); os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ @@ -802,11 +868,14 @@ 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 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, int *vlan_id) { struct hostapd_wpa_psk *psk; int next_ok = prev_psk == NULL; + if (vlan_id) + *vlan_id = 0; + 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", @@ -824,8 +893,11 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, (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))) + 0))) { + if (vlan_id) + *vlan_id = psk->vlan_id; return psk->psk; + } if (psk->psk == prev_psk) next_ok = 1; @@ -1003,6 +1075,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OCV + if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION && + bss->ocv) { + wpa_printf(MSG_ERROR, + "OCV: PMF needs to be enabled whenever using OCV"); + return -1; + } +#endif /* CONFIG_OCV */ + return 0; } @@ -1146,3 +1227,26 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, } } } + + +int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf) +{ + int with_id = 0, without_id = 0; + struct sae_password_entry *pw; + + if (conf->ssid.wpa_passphrase) + without_id = 1; + + for (pw = conf->sae_passwords; pw; pw = pw->next) { + if (pw->identifier) + with_id = 1; + else + without_id = 1; + if (with_id && without_id) + break; + } + + if (with_id && !without_id) + return 2; + return with_id; +} diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 778366d49afe..509677a45f05 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -42,6 +42,7 @@ struct mesh_conf { #define MESH_CONF_SEC_AMPE BIT(2) unsigned int security; enum mfp_options ieee80211w; + int ocv; unsigned int pairwise_cipher; unsigned int group_cipher; unsigned int mgmt_group_cipher; @@ -122,6 +123,7 @@ struct hostapd_vlan { int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ struct vlan_description vlan_desc; char ifname[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; int configured; int dynamic_vlan; #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -132,6 +134,7 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +#define KEYID_LEN 32 #define MIN_PASSPHRASE_LEN 8 #define MAX_PASSPHRASE_LEN 63 struct hostapd_sta_wpa_psk_short { @@ -145,9 +148,11 @@ struct hostapd_sta_wpa_psk_short { struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; + char keyid[KEYID_LEN]; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + int vlan_id; }; struct hostapd_eap_user { @@ -244,6 +249,7 @@ struct sae_password_entry { char *password; char *identifier; u8 peer_addr[ETH_ALEN]; + int vlan_id; }; /** @@ -335,6 +341,9 @@ struct hostapd_bss_config { /* dot11AssociationSAQueryRetryTimeout (in TUs) */ int assoc_sa_query_retry_timeout; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + int ocv; /* Operating Channel Validation */ +#endif /* CONFIG_OCV */ enum { PSK_RADIUS_IGNORED = 0, PSK_RADIUS_ACCEPTED = 1, @@ -383,13 +392,17 @@ struct hostapd_bss_config { char *server_cert; char *private_key; char *private_key_passwd; + char *check_cert_subject; int check_crl; + int check_crl_strict; + unsigned int crl_reload_interval; unsigned int tls_session_lifetime; unsigned int tls_flags; char *ocsp_stapling_response; char *ocsp_stapling_response_multi; char *dh_file; char *openssl_ciphers; + char *openssl_ecdh_curves; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -452,9 +465,11 @@ struct hostapd_bss_config { u8 *extra_cred; size_t extra_cred_len; int wps_cred_processing; + int wps_cred_add_sae; int force_per_enrollee_psk; u8 *ap_settings; size_t ap_settings_len; + struct hostapd_ssid multi_ap_backhaul_ssid; char *upnp_iface; char *friendly_name; char *manufacturer_url; @@ -557,6 +572,7 @@ struct hostapd_bss_config { int na_mcast_to_ucast; #ifdef CONFIG_HS20 int hs20; + int hs20_release; int disable_dgaf; u16 anqp_domain_id; unsigned int hs20_oper_friendly_name_count; @@ -596,6 +612,7 @@ struct hostapd_bss_config { unsigned int hs20_deauth_req_timeout; char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; char *t_c_filename; u32 t_c_timestamp; char *t_c_server_url; @@ -686,6 +703,12 @@ struct hostapd_bss_config { #endif /* CONFIG_OWE */ int coloc_intf_reporting; + + u8 send_probe_response; + +#define BACKHAUL_BSS 1 +#define FRONTHAUL_BSS 2 + int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */ }; /** @@ -717,7 +740,6 @@ struct hostapd_config { u16 beacon_int; int rts_threshold; int fragm_threshold; - u8 send_probe_response; u8 channel; u8 acs; struct wpa_freq_range_list acs_ch_list; @@ -829,12 +851,16 @@ struct hostapd_config { #ifdef CONFIG_IEEE80211AX struct he_phy_capabilities_info he_phy_capab; struct he_operation he_op; + struct ieee80211_he_mu_edca_parameter_set he_mu_edca; #endif /* CONFIG_IEEE80211AX */ /* VHT enable/disable config from CHAN_SWITCH */ #define CH_SWITCH_VHT_ENABLED BIT(0) #define CH_SWITCH_VHT_DISABLED BIT(1) unsigned int ch_switch_vht_config; + + int rssi_reject_assoc_rssi; + int rssi_reject_assoc_timeout; }; @@ -851,7 +877,7 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, int hostapd_rate_found(int *list, int rate); const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk); + const u8 *prev_psk, int *vlan_id); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); int hostapd_vlan_valid(struct hostapd_vlan *vlan, struct vlan_description *vlan_desc); @@ -862,5 +888,6 @@ 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); +int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf); #endif /* HOSTAPD_CONFIG_H */ diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index db93fde7d6e3..de40171e18dc 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -356,4 +356,22 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) return hapd->driver->stop_ap(hapd->drv_priv); } +static inline int hostapd_drv_channel_info(struct hostapd_data *hapd, + struct wpa_channel_info *ci) +{ + if (!hapd->driver || !hapd->driver->channel_info) + return -1; + return hapd->driver->channel_info(hapd->drv_priv, ci); +} + +static inline int +hostapd_drv_send_external_auth_status(struct hostapd_data *hapd, + struct external_auth *params) +{ + if (!hapd->driver || !hapd->drv_priv || + !hapd->driver->send_external_auth_status) + return -1; + return hapd->driver->send_external_auth_status(hapd->drv_priv, params); +} + #endif /* AP_DRV_OPS */ diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 95d004ed2b16..eced6c7c6d94 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -136,6 +136,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #ifdef CONFIG_HS20 srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; + srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url; srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; @@ -200,6 +201,16 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + if (hapd->conf->crl_reload_interval > 0 && + hapd->conf->check_crl <= 0) { + wpa_printf(MSG_INFO, + "Cannot enable CRL reload functionality - it depends on check_crl being set"); + } else if (hapd->conf->crl_reload_interval > 0) { + conf.crl_reload_interval = + hapd->conf->crl_reload_interval; + wpa_printf(MSG_INFO, + "Enabled CRL reload functionality"); + } conf.tls_flags = hapd->conf->tls_flags; conf.event_cb = authsrv_tls_event; conf.cb_ctx = hapd; @@ -217,10 +228,12 @@ int authsrv_init(struct hostapd_data *hapd) params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; params.openssl_ciphers = hapd->conf->openssl_ciphers; + params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; params.ocsp_stapling_response_multi = hapd->conf->ocsp_stapling_response_multi; + params.check_cert_subject = hapd->conf->check_cert_subject; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); @@ -229,7 +242,8 @@ int authsrv_init(struct hostapd_data *hapd) } if (tls_global_set_verify(hapd->ssl_ctx, - hapd->conf->check_crl)) { + hapd->conf->check_crl, + hapd->conf->check_crl_strict)) { wpa_printf(MSG_ERROR, "Failed to enable check_crl"); authsrv_deinit(hapd); return -1; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 59bd4af395d7..3e62991d07af 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -397,7 +397,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax) { buflen += 3 + sizeof(struct ieee80211_he_capabilities) + - 3 + sizeof(struct ieee80211_he_operation); + 3 + sizeof(struct ieee80211_he_operation) + + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); } #endif /* CONFIG_IEEE80211AX */ @@ -510,6 +511,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, if (hapd->iconf->ieee80211ax) { pos = hostapd_eid_he_capab(hapd, pos); pos = hostapd_eid_he_operation(hapd, pos); + pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos); } #endif /* CONFIG_IEEE80211AX */ @@ -767,7 +769,7 @@ void handle_probe_req(struct hostapd_data *hapd, ie, ie_len, ssi_signal) > 0) return; - if (!hapd->iconf->send_probe_response) + if (!hapd->conf->send_probe_response) return; if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { @@ -1085,7 +1087,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AX if (hapd->iconf->ieee80211ax) { tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + - 3 + sizeof(struct ieee80211_he_operation); + 3 + sizeof(struct ieee80211_he_operation) + + 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set); } #endif /* CONFIG_IEEE80211AX */ @@ -1222,6 +1225,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, if (hapd->iconf->ieee80211ax) { tailpos = hostapd_eid_he_capab(hapd, tailpos); tailpos = hostapd_eid_he_operation(hapd, tailpos); + tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos); } #endif /* CONFIG_IEEE80211AX */ @@ -1357,6 +1361,18 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ params->multicast_to_unicast = hapd->conf->multicast_to_unicast; params->pbss = hapd->conf->pbss; + + if (hapd->conf->ftm_responder) { + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) { + params->ftm_responder = 1; + params->lci = hapd->iface->conf->lci; + params->civic = hapd->iface->conf->civic; + } else { + wpa_printf(MSG_WARNING, + "Not configuring FTM responder as the driver doesn't advertise support for it"); + } + } + return 0; } diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 21b813ee18d7..c69371511634 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-2014, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -207,6 +207,7 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { int len, res, ret, i; + const char *keyid; if (!sta) return 0; @@ -341,6 +342,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += ret; } + keyid = ap_sta_wpa_get_keyid(hapd, sta); + if (keyid) { + ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + return len; } @@ -443,11 +451,11 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, if (stype == WLAN_FC_STYPE_DEAUTH) { mgmt->u.deauth.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); + pos = mgmt->u.deauth.variable; } else { mgmt->u.disassoc.reason_code = host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); + pos = mgmt->u.disassoc.variable; } *pos++ = WLAN_EID_VENDOR_SPECIFIC; diff --git a/src/ap/dfs.c b/src/ap/dfs.c index 993dd19c2cb0..79cd00f44a78 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -142,18 +142,30 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, { struct hostapd_channel_data *first_chan, *chan; int i; + u32 bw = num_chan_to_bw(num_chans); if (first_chan_idx + num_chans > mode->num_channels) return 0; first_chan = &mode->channels[first_chan_idx]; + /* hostapd DFS implementation assumes the first channel as primary. + * If it's not allowed to use the first channel as primary, decline the + * whole channel range. */ + if (!chan_pri_allowed(first_chan)) + return 0; + for (i = 0; i < num_chans; i++) { chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, first_chan_idx); if (!chan) return 0; + /* HT 40 MHz secondary channel availability checked only for + * primary channel */ + if (!chan_bw_allowed(chan, bw, 1, !i)) + return 0; + if (!dfs_channel_available(chan, skip_radar)) return 0; } @@ -197,7 +209,8 @@ static int dfs_find_channel(struct hostapd_iface *iface, /* Skip HT40/VHT incompatible channels */ if (iface->conf->ieee80211n && iface->conf->secondary_channel && - !dfs_is_chan_allowed(chan, n_chans)) + (!dfs_is_chan_allowed(chan, n_chans) || + !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) continue; /* Skip incompatible chandefs */ diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c index 6d8c2f4be0da..ed37fc8fe96a 100644 --- a/src/ap/dhcp_snoop.c +++ b/src/ap/dhcp_snoop.c @@ -88,6 +88,15 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, } } + 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); + } + } + if (msgtype == DHCPACK) { if (b->your_ip == 0) return; @@ -124,15 +133,6 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, } 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); - } - } } diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 149f389f789f..75edbc909e7a 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -28,34 +28,6 @@ static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -static struct dpp_configurator * -hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id) -{ - struct dpp_configurator *conf; - - dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id == id) - return conf; - } - return NULL; -} - - -static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd) -{ - struct dpp_bootstrap_info *bi; - unsigned int max_id = 0; - - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (bi->id > max_id) - max_id = bi->id; - } - return max_id + 1; -} - - /** * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code * @hapd: Pointer to hostapd_data @@ -67,13 +39,10 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) struct dpp_bootstrap_info *bi; struct dpp_authentication *auth = hapd->dpp_auth; - bi = dpp_parse_qr_code(cmd); + bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd); if (!bi) return -1; - bi->id = hapd_dpp_next_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); - if (auth && auth->response_pending && dpp_notify_new_qr_code(auth, bi) == 1) { wpa_printf(MSG_DEBUG, @@ -92,195 +61,6 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) } -static char * get_param(const char *cmd, const char *param) -{ - const char *pos, *end; - char *val; - size_t len; - - pos = os_strstr(cmd, param); - if (!pos) - return NULL; - - pos += os_strlen(param); - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - val = os_malloc(len + 1); - if (!val) - return NULL; - os_memcpy(val, pos, len); - val[len] = '\0'; - return val; -} - - -int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd) -{ - char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - size_t len; - int ret = -1; - struct dpp_bootstrap_info *bi; - - bi = os_zalloc(sizeof(*bi)); - if (!bi) - goto fail; - - if (os_strstr(cmd, "type=qrcode")) - bi->type = DPP_BOOTSTRAP_QR_CODE; - else if (os_strstr(cmd, "type=pkex")) - bi->type = DPP_BOOTSTRAP_PKEX; - else - goto fail; - - chan = get_param(cmd, " chan="); - mac = get_param(cmd, " mac="); - info = get_param(cmd, " info="); - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - pk = dpp_keygen(bi, curve, privkey, privkey_len); - if (!pk) - goto fail; - - len = 4; /* "DPP:" */ - if (chan) { - if (dpp_parse_uri_chan_list(bi, chan) < 0) - goto fail; - len += 3 + os_strlen(chan); /* C:...; */ - } - if (mac) { - if (dpp_parse_uri_mac(bi, mac) < 0) - goto fail; - len += 3 + os_strlen(mac); /* M:...; */ - } - if (info) { - if (dpp_parse_uri_info(bi, info) < 0) - goto fail; - len += 3 + os_strlen(info); /* I:...; */ - } - len += 4 + os_strlen(pk); - bi->uri = os_malloc(len + 1); - if (!bi->uri) - goto fail; - os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", - chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", - mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", - info ? "I:" : "", info ? info : "", info ? ";" : "", - pk); - bi->id = hapd_dpp_next_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); - ret = bi->id; - bi = NULL; -fail: - os_free(curve); - os_free(pk); - os_free(chan); - os_free(mac); - os_free(info); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_bootstrap_info_free(bi); - return ret; -} - - -static struct dpp_bootstrap_info * -dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (bi->id == id) - return bi; - } - return NULL; -} - - -static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id) -{ - struct dpp_bootstrap_info *bi, *tmp; - int found = 0; - - dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (id && bi->id != id) - continue; - found = 1; - dl_list_del(&bi->list); - dpp_bootstrap_info_free(bi); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_bootstrap_del(hapd->iface->interfaces, id_val); -} - - -const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, - unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(hapd, id); - if (!bi) - return NULL; - return bi->uri; -} - - -int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, - char *reply, int reply_size) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(hapd, id); - if (!bi) - return -1; - return os_snprintf(reply, reply_size, "type=%s\n" - "mac_addr=" MACSTR "\n" - "info=%s\n" - "num_freq=%u\n" - "curve=%s\n", - dpp_bootstrap_type_txt(bi->type), - MAC2STR(bi->mac_addr), - bi->info ? bi->info : "", - bi->num_freq, - bi->curve->name); -} - - static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx) { @@ -354,6 +134,16 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, return; } +#ifdef CONFIG_DPP2 + if (auth->connect_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Complete exchange on configuration result"); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } +#endif /* CONFIG_DPP2 */ + if (hapd->dpp_auth->remove_on_tx_status) { wpa_printf(MSG_DEBUG, "DPP: Terminate authentication exchange due to an earlier error"); @@ -505,178 +295,6 @@ static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, } -static int hostapd_dpp_set_configurator(struct hostapd_data *hapd, - struct dpp_authentication *auth, - const char *cmd) -{ - const char *pos, *end; - struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; - struct dpp_configurator *conf = NULL; - u8 ssid[32] = { "test" }; - size_t ssid_len = 4; - char pass[64] = { }; - size_t pass_len = 0; - u8 psk[PMK_LEN]; - int psk_set = 0; - char *group_id = NULL; - - if (!cmd) - return 0; - - wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); - pos = os_strstr(cmd, " ssid="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); - ssid_len /= 2; - if (ssid_len > sizeof(ssid) || - hexstr2bin(pos, ssid, ssid_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " pass="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - pass_len = end ? (size_t) (end - pos) : os_strlen(pos); - pass_len /= 2; - if (pass_len > sizeof(pass) - 1 || pass_len < 8 || - hexstr2bin(pos, (u8 *) pass, pass_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " psk="); - if (pos) { - pos += 5; - if (hexstr2bin(pos, psk, PMK_LEN) < 0) - goto fail; - psk_set = 1; - } - - pos = os_strstr(cmd, " group_id="); - if (pos) { - size_t group_id_len; - - pos += 10; - end = os_strchr(pos, ' '); - group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); - group_id = os_malloc(group_id_len + 1); - if (!group_id) - goto fail; - os_memcpy(group_id, pos, group_id_len); - group_id[group_id_len] = '\0'; - } - - if (os_strstr(cmd, " conf=sta-")) { - conf_sta = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_sta) - goto fail; - os_memcpy(conf_sta->ssid, ssid, ssid_len); - conf_sta->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=sta-psk") || - os_strstr(cmd, " conf=sta-sae") || - os_strstr(cmd, " conf=sta-psk-sae")) { - if (os_strstr(cmd, " conf=sta-psk-sae")) - conf_sta->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=sta-sae")) - conf_sta->akm = DPP_AKM_SAE; - else - conf_sta->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_sta->psk, psk, PMK_LEN); - } else { - conf_sta->passphrase = os_strdup(pass); - if (!conf_sta->passphrase) - goto fail; - } - } else if (os_strstr(cmd, " conf=sta-dpp")) { - conf_sta->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_sta->group_id = group_id; - group_id = NULL; - } - } - - if (os_strstr(cmd, " conf=ap-")) { - conf_ap = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_ap) - goto fail; - os_memcpy(conf_ap->ssid, ssid, ssid_len); - conf_ap->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=ap-psk") || - os_strstr(cmd, " conf=ap-sae") || - os_strstr(cmd, " conf=ap-psk-sae")) { - if (os_strstr(cmd, " conf=ap-psk-sae")) - conf_ap->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=ap-sae")) - conf_ap->akm = DPP_AKM_SAE; - else - conf_ap->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_ap->psk, psk, PMK_LEN); - } else if (pass_len > 0) { - conf_ap->passphrase = os_strdup(pass); - if (!conf_ap->passphrase) - goto fail; - } else { - goto fail; - } - } else if (os_strstr(cmd, " conf=ap-dpp")) { - conf_ap->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_ap->group_id = group_id; - group_id = NULL; - } - } - - pos = os_strstr(cmd, " expiry="); - if (pos) { - long int val; - - pos += 8; - val = strtol(pos, NULL, 0); - if (val <= 0) - goto fail; - if (conf_sta) - conf_sta->netaccesskey_expiry = val; - if (conf_ap) - conf_ap->netaccesskey_expiry = val; - } - - pos = os_strstr(cmd, " configurator="); - if (pos) { - auth->configurator = 1; - pos += 14; - conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos)); - if (!conf) { - wpa_printf(MSG_INFO, - "DPP: Could not find the specified configurator"); - goto fail; - } - } - auth->conf_sta = conf_sta; - auth->conf_ap = conf_ap; - auth->conf = conf; - os_free(group_id); - return 0; - -fail: - wpa_msg(hapd->msg_ctx, MSG_INFO, - "DPP: Failed to set configurator parameters"); - dpp_configuration_free(conf_sta); - dpp_configuration_free(conf_ap); - os_free(group_id); - return -1; -} - - static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; @@ -786,7 +404,7 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (!pos) return -1; pos += 6; - peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); if (!peer_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified peer"); @@ -796,7 +414,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) pos = os_strstr(cmd, " own="); if (pos) { pos += 5; - own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, + atoi(pos)); if (!own_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified local entry"); @@ -846,7 +465,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (!hapd->dpp_auth) goto fail; hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); - if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) { + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + hapd->dpp_auth, cmd) < 0) { dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; goto fail; @@ -905,7 +525,10 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, { const u8 *r_bootstrap, *i_bootstrap; u16 r_bootstrap_len, i_bootstrap_len; - struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; + + if (!hapd->iface->interfaces->dpp) + return; wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, MAC2STR(src)); @@ -932,28 +555,8 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, /* Try to find own and peer bootstrapping key matches based on the * received hash values */ - dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (!own_bi && bi->own && - os_memcmp(bi->pubkey_hash, r_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching own bootstrapping information"); - own_bi = bi; - } - - if (!peer_bi && !bi->own && - os_memcmp(bi->pubkey_hash, i_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching peer bootstrapping information"); - peer_bi = bi; - } - - if (own_bi && peer_bi) - break; - } - + dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap, + r_bootstrap, &own_bi, &peer_bi); if (!own_bi) { wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "No matching own bootstrapping key found - ignore message"); @@ -975,8 +578,9 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src, return; } hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); - if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, - hapd->dpp_configurator_params) < 0) { + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + hapd->dpp_auth, + hapd->dpp_configurator_params) < 0) { dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; return; @@ -1072,6 +676,7 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, struct hostapd_data *hapd = ctx; const u8 *pos; struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED; if (!auth || !auth->auth_success) { wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); @@ -1107,12 +712,41 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, } hostapd_dpp_handle_config_obj(hapd, auth); - dpp_auth_deinit(hapd->dpp_auth); - hapd->dpp_auth = NULL; - return; - + status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_REJECT_CONFIG) { + wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object"); + status = DPP_STATUS_CONFIG_REJECTED; + } +#endif /* CONFIG_TESTING_OPTIONS */ fail: - wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + if (status != DPP_STATUS_OK) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); +#ifdef CONFIG_DPP2 + if (auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); + msg = dpp_build_conf_result(auth, status); + if (!msg) + goto fail2; + + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(addr), auth->curr_freq, + DPP_PA_CONFIGURATION_RESULT); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, + addr, wpabuf_head(msg), + wpabuf_len(msg)); + wpabuf_free(msg); + + /* This exchange will be terminated in the TX status handler */ + auth->connect_on_tx_status = 1; + return; + } +fail2: +#endif /* CONFIG_DPP2 */ dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; } @@ -1121,7 +755,7 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) { struct dpp_authentication *auth = hapd->dpp_auth; - struct wpabuf *buf, *conf_req; + struct wpabuf *buf; char json[100]; int res; int netrole_ap = 1; @@ -1133,34 +767,13 @@ static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) netrole_ap ? "ap" : "sta"); wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); - conf_req = dpp_build_conf_req(auth, json); - if (!conf_req) { + buf = dpp_build_conf_req(auth, json); + if (!buf) { wpa_printf(MSG_DEBUG, "DPP: No configuration request data available"); return; } - buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); - if (!buf) { - wpabuf_free(conf_req); - return; - } - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 8); /* Length */ - wpabuf_put_u8(buf, 0x7f); - wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, DPP_OUI_TYPE); - wpabuf_put_u8(buf, 0x01); - - /* GAS Query */ - wpabuf_put_le16(buf, wpabuf_len(conf_req)); - wpabuf_put_buf(buf, conf_req); - wpabuf_free(conf_req); - wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", MAC2STR(auth->peer_mac_addr), auth->curr_freq); @@ -1281,6 +894,63 @@ static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, } +#ifdef CONFIG_DPP2 + +static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Configuration Result"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; +} + + +static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status; + + wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR, + MAC2STR(src)); + + if (!auth || !auth->waiting_conf_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for result - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + status = dpp_conf_result_rx(auth, hdr, buf, len); + + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + if (status == DPP_STATUS_OK) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, + NULL); +} + +#endif /* CONFIG_DPP2 */ + + static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd, const u8 *src, unsigned int freq, u8 trans_id, @@ -1596,24 +1266,10 @@ hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, wpabuf_head(msg), wpabuf_len(msg)); wpabuf_free(msg); - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); if (!bi) return; - bi->id = hapd_dpp_next_id(hapd); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, src, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = pkex->own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); hapd->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return; - } - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); } @@ -1623,7 +1279,7 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, unsigned int freq) { int res; - struct dpp_bootstrap_info *bi, *own_bi; + struct dpp_bootstrap_info *bi; struct dpp_pkex *pkex = hapd->dpp_pkex; char cmd[500]; @@ -1641,26 +1297,10 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, return; } - own_bi = pkex->own_bi; - - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); if (!bi) return; - bi->id = hapd_dpp_next_id(hapd); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, src, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); hapd->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return; - } - dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); os_snprintf(cmd, sizeof(cmd), " peer=%u %s", bi->id, @@ -1744,6 +1384,11 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len, freq); break; +#ifdef CONFIG_DPP2 + case DPP_PA_CONFIGURATION_RESULT: + hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len); + break; +#endif /* CONFIG_DPP2 */ default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -1790,11 +1435,28 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) { - if (!hapd->dpp_auth) + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth) return; + wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", + ok); eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +#ifdef CONFIG_DPP2 + if (ok && auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); + auth->waiting_conf_result = 1; + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + eloop_register_timeout(2, 0, + hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + return; + } +#endif /* CONFIG_DPP2 */ hostapd_drv_send_action_cancel_wait(hapd); if (ok) @@ -1806,93 +1468,6 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) } -static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd) -{ - struct dpp_configurator *conf; - unsigned int max_id = 0; - - dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id > max_id) - max_id = conf->id; - } - return max_id + 1; -} - - -int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd) -{ - char *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - int ret = -1; - struct dpp_configurator *conf = NULL; - - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - conf = dpp_keygen_configurator(curve, privkey, privkey_len); - if (!conf) - goto fail; - - conf->id = hostapd_dpp_next_configurator_id(hapd); - dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list); - ret = conf->id; - conf = NULL; -fail: - os_free(curve); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_configurator_free(conf); - return ret; -} - - -static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id) -{ - struct dpp_configurator *conf, *tmp; - int found = 0; - - dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator, - struct dpp_configurator, list) { - if (id && conf->id != id) - continue; - found = 1; - dl_list_del(&conf->list); - dpp_configurator_free(conf); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_configurator_del(hapd->iface->interfaces, id_val); -} - - int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) { struct dpp_authentication *auth; @@ -1905,7 +1480,8 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) curve = get_param(cmd, " curve="); hostapd_dpp_set_testing_options(hapd, auth); - if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 && + if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx, + auth, cmd) == 0 && dpp_configurator_own_config(auth, curve, 1) == 0) { hostapd_dpp_handle_config_obj(hapd, auth); ret = 0; @@ -1918,19 +1494,6 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd) } -int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, - char *buf, size_t buflen) -{ - struct dpp_configurator *conf; - - conf = hostapd_dpp_configurator_get_id(hapd, id); - if (!conf) - return -1; - - return dpp_configurator_get_key(conf, buf, buflen); -} - - int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) { struct dpp_bootstrap_info *own_bi; @@ -1940,7 +1503,7 @@ int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) if (!pos) return -1; pos += 5; - own_bi = dpp_bootstrap_get_id(hapd, atoi(pos)); + own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos)); if (!own_bi) { wpa_printf(MSG_DEBUG, "DPP: Identified bootstrap info not found"); @@ -2070,6 +1633,10 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +#ifdef CONFIG_DPP2 + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, + NULL); +#endif /* CONFIG_DPP2 */ dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; hostapd_dpp_pkex_remove(hapd, "*"); @@ -2077,20 +1644,3 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) os_free(hapd->dpp_configurator_params); hapd->dpp_configurator_params = NULL; } - - -void hostapd_dpp_init_global(struct hapd_interfaces *ifaces) -{ - dl_list_init(&ifaces->dpp_bootstrap); - dl_list_init(&ifaces->dpp_configurator); - ifaces->dpp_init_done = 1; -} - - -void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces) -{ - if (!ifaces->dpp_init_done) - return; - dpp_bootstrap_del(ifaces, 0); - dpp_configurator_del(ifaces, 0); -} diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h index 3ef7c14567e8..449ca16d118f 100644 --- a/src/ap/dpp_hostapd.h +++ b/src/ap/dpp_hostapd.h @@ -10,12 +10,6 @@ #define DPP_HOSTAPD_H int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); -int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd); -int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id); -const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, - unsigned int id); -int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, - char *reply, int reply_size); int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); void hostapd_dpp_listen_stop(struct hostapd_data *hapd); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index a726a6ff87e0..8ddf754f60a7 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -15,6 +15,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "common/dpp.h" #include "crypto/random.h" #include "p2p/p2p.h" #include "wps/wps.h" @@ -38,6 +39,7 @@ #include "mbo_ap.h" #include "dpp_hostapd.h" #include "fils_hlp.h" +#include "neighbor_db.h" #ifdef CONFIG_FILS @@ -304,6 +306,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return -1; } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, ie, ielen, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); @@ -563,6 +566,38 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + hapd->conf->dpp_netaccesskey && sta->wpa_sm && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && + elems.owe_dh) { + sta->dpp_pfs = dpp_pfs_init( + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!sta->dpp_pfs) { + wpa_printf(MSG_DEBUG, + "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + + if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + } + + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); + pfs_fail: +#endif /* CONFIG_DPP2 */ + #if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE) hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); @@ -739,9 +774,12 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2) { + /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */ + #ifdef NEED_AP_MLME int channel, chwidth, is_dfs; u8 seg0_idx = 0, seg1_idx = 0; + size_t i; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, @@ -824,6 +862,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d dfs=%d", freq, is_dfs); } + + for (i = 0; i < hapd->iface->num_bss; i++) + hostapd_neighbor_set_own_report(hapd->iface->bss[i]); #endif /* NEED_AP_MLME */ } @@ -1065,6 +1106,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, } +#ifndef NEED_AP_MLME static void hostapd_action_rx(struct hostapd_data *hapd, struct rx_mgmt *drv_mgmt) { @@ -1077,7 +1119,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) return; - plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1; + plen = drv_mgmt->frame_len - IEEE80211_HDRLEN; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); @@ -1097,22 +1139,20 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #ifdef CONFIG_IEEE80211R_AP 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); + wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen); + return; } #endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W - 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); + if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) { + ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len); + return; } #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM_AP if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); + return; } #endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST @@ -1122,7 +1162,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #endif /* CONFIG_FST */ #ifdef CONFIG_DPP - if (plen >= 1 + 4 && + if (plen >= 2 + 4 && mgmt->u.action.u.vs_public_action.action == WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == @@ -1139,6 +1179,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd, } #endif /* CONFIG_DPP */ } +#endif /* NEED_AP_MLME */ #ifdef NEED_AP_MLME @@ -1600,10 +1641,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, 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_mgmt_rx(hapd, &data->rx_mgmt); +#else /* NEED_AP_MLME */ hostapd_action_rx(hapd, &data->rx_mgmt); +#endif /* NEED_AP_MLME */ break; case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || @@ -1725,6 +1766,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hostapd_reconfig_encryption(hapd); hapd->reenable_beacon = 1; ieee802_11_set_beacon(hapd); +#ifdef NEED_AP_MLME + } else if (hapd->disabled && hapd->iface->cac_started) { + wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC"); + hostapd_handle_dfs(hapd->iface); +#endif /* NEED_AP_MLME */ } break; case EVENT_INTERFACE_DISABLED: diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 296d5c2ddf31..a510ee3e29fd 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -139,6 +139,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, struct hostapd_eap_user *user = NULL; char id_str[256], cmd[300]; size_t i; + int res; if (identity_len >= sizeof(id_str)) { wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", @@ -174,6 +175,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, if (hapd->tmp_eap_user.identity == NULL) return NULL; os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + hapd->tmp_eap_user.identity_len = identity_len; if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", @@ -182,9 +184,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, return NULL; } - os_snprintf(cmd, sizeof(cmd), - "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", - id_str, phase2); + res = os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", + id_str, phase2); + if (os_snprintf_error(sizeof(cmd), res)) + goto fail; + wpa_printf(MSG_DEBUG, "DB: %s", cmd); if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != SQLITE_OK) { @@ -214,6 +219,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, } } +fail: sqlite3_close(db); return user; diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c index 2a359ab03c81..6da514a4d0fb 100644 --- a/src/ap/fils_hlp.c +++ b/src/ap/fils_hlp.c @@ -580,6 +580,19 @@ int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, u8 *tmp, *tmp_pos; int ret = 0; + if (sta->fils_pending_assoc_req && + eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { + /* Do not process FILS HLP request again if the station + * retransmits (Re)Association Request frame before the previous + * HLP response has either been received or timed out. */ + wpa_printf(MSG_DEBUG, + "FILS: Do not relay another HLP request from " + MACSTR + " before processing of the already pending one has been completed", + MAC2STR(sta->addr)); + return 1; + } + /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ wpabuf_free(sta->hlp_dhcp_discover); sta->hlp_dhcp_discover = NULL; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 7501bac6e42a..20c8e8f5a4f7 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -348,10 +348,11 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) if (!hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", - __func__, hapd->conf->iface); + __func__, hapd->conf ? hapd->conf->iface : "N/A"); return; } hapd->started = 0; + hapd->beacon_set_done = 0; wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); iapp_deinit(hapd->iapp); @@ -417,6 +418,20 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) hostapd_clean_rrm(hapd); fils_hlp_deinit(hapd); + +#ifdef CONFIG_SAE + { + struct hostapd_sae_commit_queue *q; + + while ((q = dl_list_first(&hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, + list))) { + dl_list_del(&q->list); + os_free(q); + } + } + eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL); +#endif /* CONFIG_SAE */ } @@ -431,7 +446,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) static void hostapd_cleanup(struct hostapd_data *hapd) { wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); if (hapd->iface->interfaces && hapd->iface->interfaces->ctrl_iface_deinit) { wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); @@ -506,7 +521,7 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) static void hostapd_clear_wep(struct hostapd_data *hapd) { - if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) { + if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) { hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); } @@ -658,8 +673,10 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) for (i = 5; i > 5 - j; i--) mask[i] = 0; j = bits % 8; - while (j--) + while (j) { + j--; mask[i] <<= 1; + } skip_mask_ext: wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", @@ -1668,127 +1685,6 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, #endif /* CONFIG_FST */ - -#ifdef NEED_AP_MLME -static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, - int ht, int vht) -{ - if (!ht && !vht) - return NR_CHAN_WIDTH_20; - if (!hapd->iconf->secondary_channel) - return NR_CHAN_WIDTH_20; - if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) - return NR_CHAN_WIDTH_40; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) - return NR_CHAN_WIDTH_80; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) - return NR_CHAN_WIDTH_160; - if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) - return NR_CHAN_WIDTH_80P80; - return NR_CHAN_WIDTH_20; -} -#endif /* NEED_AP_MLME */ - - -static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) -{ -#ifdef NEED_AP_MLME - u16 capab = hostapd_own_capab_info(hapd); - int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; - int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; - struct wpa_ssid_value ssid; - u8 channel, op_class; - u8 center_freq1_idx = 0, center_freq2_idx = 0; - enum nr_chan_width width; - u32 bssid_info; - struct wpabuf *nr; - - if (!(hapd->conf->radio_measurements[0] & - WLAN_RRM_CAPS_NEIGHBOR_REPORT)) - return; - - bssid_info = 3; /* AP is reachable */ - bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ - bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ - - if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) - bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; - - bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ - - if (hapd->conf->wmm_enabled) { - bssid_info |= NEI_REP_BSSID_INFO_QOS; - - if (hapd->conf->wmm_uapsd && - (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) - bssid_info |= NEI_REP_BSSID_INFO_APSD; - } - - if (ht) { - bssid_info |= NEI_REP_BSSID_INFO_HT | - NEI_REP_BSSID_INFO_DELAYED_BA; - - /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ - if (vht) - bssid_info |= NEI_REP_BSSID_INFO_VHT; - } - - /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ - - if (ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, - &op_class, &channel) == - NUM_HOSTAPD_MODES) - return; - width = hostapd_get_nr_chan_width(hapd, ht, vht); - if (vht) { - center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; - if (width == NR_CHAN_WIDTH_80P80) - center_freq2_idx = - hapd->iconf->vht_oper_centr_freq_seg1_idx; - } else if (ht) { - ieee80211_freq_to_chan(hapd->iface->freq + - 10 * hapd->iconf->secondary_channel, - ¢er_freq1_idx); - } - - ssid.ssid_len = hapd->conf->ssid.ssid_len; - os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); - - /* - * Neighbor Report element size = BSSID + BSSID info + op_class + chan + - * phy type + wide bandwidth channel subelement. - */ - nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); - if (!nr) - return; - - wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); - wpabuf_put_le32(nr, bssid_info); - wpabuf_put_u8(nr, op_class); - wpabuf_put_u8(nr, channel); - wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); - - /* - * Wide Bandwidth Channel subelement may be needed to allow the - * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 - * Figure 9-301. - */ - wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); - wpabuf_put_u8(nr, 3); - wpabuf_put_u8(nr, width); - wpabuf_put_u8(nr, center_freq1_idx); - wpabuf_put_u8(nr, center_freq2_idx); - - hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, - hapd->iconf->civic, hapd->iconf->stationary_ap); - - wpabuf_free(nr); -#endif /* NEED_AP_MLME */ -} - - #ifdef CONFIG_OWE static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) @@ -1988,15 +1884,17 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, } } - if (hapd->iconf->rts_threshold > -1 && - hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { + if (hapd->iconf->rts_threshold >= -1 && + hostapd_set_rts(hapd, hapd->iconf->rts_threshold) && + hapd->iconf->rts_threshold >= -1) { wpa_printf(MSG_ERROR, "Could not set RTS threshold for " "kernel driver"); goto fail; } - if (hapd->iconf->fragm_threshold > -1 && - hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { + if (hapd->iconf->fragm_threshold >= -1 && + hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) && + hapd->iconf->fragm_threshold != -1) { wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " "for kernel driver"); goto fail; @@ -2009,11 +1907,14 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, if (j) os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); if (hostapd_setup_bss(hapd, j == 0)) { - do { + for (;;) { hapd = iface->bss[j]; hostapd_bss_deinit_no_free(hapd); hostapd_free_hapd_data(hapd); - } while (j-- > 0); + if (j == 0) + break; + j--; + } goto fail; } if (is_zero_ether_addr(hapd->conf->bssid)) @@ -2085,7 +1986,7 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, iface->interfaces->terminate_on_error--; for (j = 0; j < iface->num_bss; j++) - hostapd_set_own_neighbor_report(iface->bss[j]); + hostapd_neighbor_set_own_report(iface->bss[j]); return 0; @@ -2266,6 +2167,9 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, dl_list_init(&hapd->l2_queue); dl_list_init(&hapd->l2_oui_queue); #endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_SAE + dl_list_init(&hapd->sae_commit_queue); +#endif /* CONFIG_SAE */ return hapd; } @@ -2276,7 +2180,7 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd) if (!hapd) return; wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, - hapd->conf->iface); + hapd->conf ? hapd->conf->iface : "N/A"); hostapd_bss_deinit_no_free(hapd); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); hostapd_cleanup(hapd); @@ -2303,7 +2207,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) } #endif /* CONFIG_FST */ - for (j = iface->num_bss - 1; j >= 0; j--) { + for (j = (int) iface->num_bss - 1; j >= 0; j--) { if (!iface->bss) break; hostapd_bss_deinit(iface->bss[j]); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index d304c1171810..790d37754870 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -66,9 +66,7 @@ struct hapd_interfaces { int eloop_initialized; #ifdef CONFIG_DPP - int dpp_init_done; - struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ - struct dl_list dpp_configurator; /* struct dpp_configurator */ + struct dpp_global *dpp; #endif /* CONFIG_DPP */ }; @@ -129,6 +127,13 @@ struct hostapd_neighbor_entry { int stationary; }; +struct hostapd_sae_commit_queue { + struct dl_list list; + int rssi; + size_t len; + u8 msg[]; +}; + /** * struct hostapd_data - hostapd per-BSS data structure */ @@ -307,7 +312,10 @@ struct hostapd_data { /** Key used for generating SAE anti-clogging tokens */ u8 sae_token_key[8]; struct os_reltime last_sae_token_key_update; + u16 sae_token_idx; + u16 sae_pending_token_idx[256]; int dot11RSNASAERetransPeriod; /* msec */ + struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */ #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS diff --git a/src/ap/hs20.c b/src/ap/hs20.c index e265569aef38..532580e7c66c 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -25,17 +25,20 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) if (!hapd->conf->hs20) return eid; *eid++ = WLAN_EID_VENDOR_SPECIFIC; - *eid++ = 7; + *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7; WPA_PUT_BE24(eid, OUI_WFA); eid += 3; *eid++ = HS20_INDICATION_OUI_TYPE; - conf = HS20_VERSION; /* Release Number */ - conf |= HS20_ANQP_DOMAIN_ID_PRESENT; + conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */ + if (hapd->conf->hs20_release >= 2) + 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; + if (hapd->conf->hs20_release >= 2) { + WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id); + eid += 2; + } return eid; } @@ -84,6 +87,10 @@ u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid) capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (hapd->conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); eid += 2; diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 5279abca1f1f..9d3d990a281f 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -229,9 +229,6 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) { int pri_chan, sec_chan; - if (!iface->conf->secondary_channel) - return 1; /* HT40 not used */ - pri_chan = iface->conf->channel; sec_chan = pri_chan + iface->conf->secondary_channel * 4; @@ -697,30 +694,25 @@ 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; if (!iface->current_mode) return 0; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; - if (chan->chan != channel) - continue; + chan = hw_get_channel_chan(iface->current_mode, channel, NULL); + if (!chan) + return 0; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) - return 1; + if ((primary && chan_pri_allowed(chan)) || + (!primary && !(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" : ""); - } - - wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode", - channel, primary ? "primary" : "secondary"); + wpa_printf(MSG_INFO, + "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", + channel, primary ? "primary" : "secondary", + chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); return 0; } @@ -728,6 +720,12 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, static int hostapd_is_usable_chans(struct hostapd_iface *iface) { int secondary_chan; + struct hostapd_channel_data *pri_chan; + + pri_chan = hw_get_channel_chan(iface->current_mode, + iface->conf->channel, NULL); + if (!pri_chan) + return 0; if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; @@ -742,13 +740,15 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) /* Both HT40+ and HT40- are set, pick a valid secondary channel */ secondary_chan = iface->conf->channel + 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { iface->conf->secondary_channel = 1; return 1; } secondary_chan = iface->conf->channel - 4; - if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + if (hostapd_is_usable_chan(iface, secondary_chan, 0) && + (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { iface->conf->secondary_channel = -1; return 1; } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index f9bb99d98549..fde19b526f05 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -21,6 +21,8 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "common/sae.h" +#include "common/dpp.h" +#include "common/ocv.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -61,6 +63,25 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, const u8 *msk, size_t msk_len, int *is_pub); #endif /* CONFIG_FILS */ +static void handle_auth(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi, int from_queue); + + +u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid) +{ + u8 multi_ap_val = 0; + + if (!hapd->conf->multi_ap) + return eid; + if (hapd->conf->multi_ap & BACKHAUL_BSS) + multi_ap_val |= MULTI_AP_BACKHAUL_BSS; + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + multi_ap_val |= MULTI_AP_FRONTHAUL_BSS; + + return eid + add_multi_ap_ie(eid, 9, multi_ap_val); +} + u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { @@ -403,6 +424,15 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, return NULL; } + if (pw && pw->vlan_id) { + if (!sta->sae->tmp) { + wpa_printf(MSG_INFO, + "SAE: No temporary data allocated - cannot store VLAN ID"); + return NULL; + } + sta->sae->tmp->vlan_id = pw->vlan_id; + } + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + (rx_id ? 3 + os_strlen(rx_id) : 0)); if (buf == NULL) @@ -492,21 +522,57 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd) return 1; } + /* In addition to already existing open SAE sessions, check whether + * there are enough pending commit messages in the processing queue to + * potentially result in too many open sessions. */ + if (open + dl_list_len(&hapd->sae_commit_queue) >= + hapd->conf->sae_anti_clogging_threshold) + return 1; + return 0; } +static u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr) +{ + u8 hash[SHA256_MAC_LEN]; + + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, hash); + return hash[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]; + const u8 *addrs[2]; + size_t len[2]; + u16 token_idx; + u8 idx; 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) + idx = sae_token_hash(hapd, addr); + token_idx = hapd->sae_pending_token_idx[idx]; + if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid anti-clogging token from " + MACSTR " - token_idx 0x%04x, expected 0x%04x", + MAC2STR(addr), WPA_GET_BE16(token), token_idx); return -1; + } + + addrs[0] = addr; + len[0] = ETH_ALEN; + addrs[1] = token; + len[1] = 2; + if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + 2, addrs, len, mac) < 0 || + os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) + return -1; + + hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */ return 0; } @@ -518,16 +584,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, struct wpabuf *buf; u8 *token; struct os_reltime now; + u8 idx[2]; + const u8 *addrs[2]; + size_t len[2]; + u8 p_idx; + u16 token_idx; 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)) { + os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) || + hapd->sae_token_idx == 0xffff) { 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; + hapd->sae_token_idx = 0; + os_memset(hapd->sae_pending_token_idx, 0, + sizeof(hapd->sae_pending_token_idx)); } buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); @@ -536,9 +611,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + p_idx = sae_token_hash(hapd, addr); + token_idx = hapd->sae_pending_token_idx[p_idx]; + if (!token_idx) { + hapd->sae_token_idx++; + token_idx = hapd->sae_token_idx; + hapd->sae_pending_token_idx[p_idx] = token_idx; + } + WPA_PUT_BE16(idx, token_idx); token = wpabuf_put(buf, SHA256_MAC_LEN); - hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), - addr, ETH_ALEN, token); + addrs[0] = addr; + len[0] = ETH_ALEN; + addrs[1] = idx; + len[1] = sizeof(idx); + if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key), + 2, addrs, len, token) < 0) { + wpabuf_free(buf); + return NULL; + } + WPA_PUT_BE16(token, token_idx); return buf; } @@ -610,8 +701,52 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, } +static void sae_sme_send_external_auth_status(struct hostapd_data *hapd, + struct sta_info *sta, u16 status) +{ + struct external_auth params; + + os_memset(¶ms, 0, sizeof(params)); + params.status = status; + params.bssid = sta->addr; + if (status == WLAN_STATUS_SUCCESS && sta->sae) + params.pmkid = sta->sae->pmkid; + + hostapd_drv_send_external_auth_status(hapd, ¶ms); +} + + void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) { +#ifndef CONFIG_NO_VLAN + struct vlan_description vlan_desc; + + if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) { + wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR + " to VLAN ID %d", + MAC2STR(sta->addr), sta->sae->tmp->vlan_id); + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = 1; + vlan_desc.untagged = sta->sae->tmp->vlan_id; + if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + wpa_printf(MSG_INFO, + "Invalid VLAN ID %d in sae_password", + sta->sae->tmp->vlan_id); + return; + } + + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 || + ap_sta_bind_vlan(hapd, sta) < 0) { + wpa_printf(MSG_INFO, + "Failed to assign VLAN ID %d from sae_password to " + MACSTR, sta->sae->tmp->vlan_id, + MAC2STR(sta->addr)); + return; + } + } +#endif /* CONFIG_NO_VLAN */ + sta->flags |= WLAN_STA_AUTH; sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); @@ -619,14 +754,18 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); + sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS); } static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *bssid, u8 auth_transaction) + const u8 *bssid, u8 auth_transaction, int allow_reuse, + int *sta_removed) { int ret; + *sta_removed = 0; + if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -636,7 +775,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, switch (sta->sae->state) { case SAE_NOTHING: if (auth_transaction == 1) { - ret = auth_sae_send_commit(hapd, sta, bssid, 1); + ret = auth_sae_send_commit(hapd, sta, bssid, + !allow_reuse); if (ret) return ret; sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); @@ -725,7 +865,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * step to get to Accepted without waiting for * additional events. */ - return sae_sm_step(hapd, sta, bssid, auth_transaction); + return sae_sm_step(hapd, sta, bssid, auth_transaction, + 0, sta_removed); } break; case SAE_CONFIRMED: @@ -758,8 +899,9 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + ap_free_sta(hapd, sta); + *sta_removed = 1; } else if (auth_transaction == 1) { wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); ret = auth_sae_send_commit(hapd, sta, bssid, 1); @@ -795,18 +937,21 @@ static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) { struct sae_data *sae = sta->sae; int i, *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; if (sae->state != SAE_COMMITTED) return; wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); - for (i = 0; groups && groups[i] > 0; i++) { + if (!groups) + groups = default_groups; + for (i = 0; groups[i] > 0; i++) { if (sae->group == groups[i]) break; } - if (!groups || groups[i] <= 0) { + if (groups[i] <= 0) { wpa_printf(MSG_DEBUG, "SAE: Previously selected group not found from the current configuration"); return; @@ -835,11 +980,16 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, { int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; + int *groups = hapd->conf->sae_groups; + int default_groups[] = { 19, 0 }; + const u8 *pos, *end; + int sta_removed = 0; + + if (!groups) + groups = default_groups; #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { - const u8 *pos, *end; - wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); pos = mgmt->u.auth.variable; end = ((const u8 *) mgmt) + len; @@ -882,8 +1032,10 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } if (auth_transaction == 1) { - const u8 *token = NULL, *pos, *end; + const u8 *token = NULL; size_t token_len = 0; + int allow_reuse = 0; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "start SAE authentication (RX commit, status=%u)", @@ -900,8 +1052,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } - resp = sae_group_allowed(sta->sae, - hapd->conf->sae_groups, + resp = sae_group_allowed(sta->sae, groups, WPA_GET_LE16(pos)); if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, @@ -962,15 +1113,28 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * to use a different group and that would not be * allowed if we remain in Committed state with the * previously set parameters. */ - sae_set_state(sta, SAE_NOTHING, - "Clear existing state to allow restart"); - sae_clear_data(sta->sae); + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + if (end - pos >= (int) sizeof(le16) && + sae_group_allowed(sta->sae, groups, + WPA_GET_LE16(pos)) == + WLAN_STATUS_SUCCESS) { + /* Do not waste resources deriving the same PWE + * again since the same group is reused. */ + sae_set_state(sta, SAE_NOTHING, + "Allow previous PWE to be reused"); + allow_reuse = 1; + } else { + sae_set_state(sta, SAE_NOTHING, + "Clear existing state to allow restart"); + sae_clear_data(sta->sae); + } } 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); + &token_len, groups); if (resp == SAE_SILENTLY_DISCARD) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", @@ -1000,7 +1164,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) goto reply; - if (!token && use_sae_anti_clogging(hapd)) { + if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) { wpa_printf(MSG_DEBUG, "SAE: Request anti-clogging token from " MACSTR, MAC2STR(sta->addr)); @@ -1013,7 +1177,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, goto reply; } - resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, + allow_reuse, &sta_removed); } else if (auth_transaction == 2) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1054,7 +1219,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } sta->sae->rc = peer_send_confirm; } - resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0, + &sta_removed); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1066,7 +1232,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } reply: - if (resp != WLAN_STATUS_SUCCESS) { + if (!sta_removed && resp != WLAN_STATUS_SUCCESS) { + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + + /* Copy the Finite Cyclic Group field from the request if we + * rejected it as unsupported group. */ + if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + !data && end - pos >= 2) + data = wpabuf_alloc_copy(pos, 2); + + sae_sme_send_external_auth_status(hapd, sta, resp); send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", @@ -1074,8 +1250,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } remove_sta: - if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || - status_code != WLAN_STATUS_SUCCESS)) { + if (!sta_removed && sta->added_unassoc && + (resp != WLAN_STATUS_SUCCESS || + status_code != WLAN_STATUS_SUCCESS)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -1114,6 +1291,105 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) return 0; } + +void auth_sae_process_commit(void *eloop_ctx, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct hostapd_sae_commit_queue *q; + unsigned int queue_len; + + q = dl_list_first(&hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list); + if (!q) + return; + wpa_printf(MSG_DEBUG, + "SAE: Process next available message from queue"); + dl_list_del(&q->list); + handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len, + q->rssi, 1); + os_free(q); + + if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) + return; + queue_len = dl_list_len(&hapd->sae_commit_queue); + eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, + hapd, NULL); +} + + +static void auth_sae_queue(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi) +{ + struct hostapd_sae_commit_queue *q, *q2; + unsigned int queue_len; + const struct ieee80211_mgmt *mgmt2; + + queue_len = dl_list_len(&hapd->sae_commit_queue); + if (queue_len >= 15) { + wpa_printf(MSG_DEBUG, + "SAE: No more room in message queue - drop the new frame from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from " + MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa), + queue_len); + q = os_zalloc(sizeof(*q) + len); + if (!q) + return; + q->rssi = rssi; + q->len = len; + os_memcpy(q->msg, mgmt, len); + + /* Check whether there is already a queued Authentication frame from the + * same station with the same transaction number and if so, replace that + * queue entry with the new one. This avoids issues with a peer that + * sends multiple times (e.g., due to frequent SAE retries). There is no + * point in us trying to process the old attempts after a new one has + * obsoleted them. */ + dl_list_for_each(q2, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt2 = (const struct ieee80211_mgmt *) q2->msg; + if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 && + mgmt->u.auth.auth_transaction == + mgmt2->u.auth.auth_transaction) { + wpa_printf(MSG_DEBUG, + "SAE: Replace queued message from same STA with same transaction number"); + dl_list_add(&q2->list, &q->list); + dl_list_del(&q2->list); + os_free(q2); + goto queued; + } + } + + /* No pending identical entry, so add to the end of the queue */ + dl_list_add_tail(&hapd->sae_commit_queue, &q->list); + +queued: + if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL)) + return; + eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit, + hapd, NULL); +} + + +static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr) +{ + struct hostapd_sae_commit_queue *q; + const struct ieee80211_mgmt *mgmt; + + dl_list_for_each(q, &hapd->sae_commit_queue, + struct hostapd_sae_commit_queue, list) { + mgmt = (const struct ieee80211_mgmt *) q->msg; + if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + #endif /* CONFIG_SAE */ @@ -1273,6 +1549,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, elems.rsn_ie - 2, elems.rsn_ie_len + 2, elems.mdie, elems.mdie_len, NULL, 0); resp = wpa_res_to_status_code(res); @@ -1544,7 +1821,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd, } sta->fils_erp_pmkid_set = 0; - if (wpa_auth_pmksa_add2( + wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len, + sta->fils_erp_pmkid); + if (!hapd->conf->disable_pmksa_caching && + wpa_auth_pmksa_add2( hapd->wpa_auth, sta->addr, pmk, pmk_len, sta->fils_erp_pmkid, @@ -1743,7 +2023,8 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, static void handle_auth(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + int rssi, int from_queue) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; @@ -1790,11 +2071,12 @@ static void handle_auth(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " "auth_transaction=%d status_code=%d wep=%d%s " - "seq_ctrl=0x%x%s", + "seq_ctrl=0x%x%s%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), challenge ? " challenge" : "", - seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "", + from_queue ? " (from queue)" : ""); #ifdef CONFIG_NO_RC4 if (auth_alg == WLAN_AUTH_SHARED_KEY) { @@ -1922,9 +2204,26 @@ static void handle_auth(struct hostapd_data *hapd, if (res == HOSTAPD_ACL_PENDING) return; +#ifdef CONFIG_SAE + if (auth_alg == WLAN_AUTH_SAE && !from_queue && + (auth_transaction == 1 || + (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) { + /* Handle SAE Authentication commit message through a queue to + * provide more control for postponing the needed heavy + * processing under a possible DoS attack scenario. In addition, + * queue SAE Authentication confirm message if there happens to + * be a queued commit message from the same peer. This is needed + * to avoid reordering Authentication frames within the same + * SAE exchange. */ + auth_sae_queue(hapd, mgmt, len, rssi); + return; + } +#endif /* CONFIG_SAE */ + sta = ap_get_sta(hapd, mgmt->sa); if (sta) { sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + sta->ft_over_ds = 0; if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && @@ -1973,6 +2272,9 @@ static void handle_auth(struct hostapd_data *hapd, } sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; +#ifdef CONFIG_MBO + sta->auth_rssi = rssi; +#endif /* CONFIG_MBO */ res = ieee802_11_set_radius_info( hapd, sta, res, session_timeout, acct_interim_interval, @@ -2210,6 +2512,59 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; } +static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *multi_ap_ie, size_t multi_ap_len) +{ + u8 multi_ap_value = 0; + + sta->flags &= ~WLAN_STA_MULTI_AP; + + if (!hapd->conf->multi_ap) + return WLAN_STATUS_SUCCESS; + + if (multi_ap_ie) { + const u8 *multi_ap_subelem; + + multi_ap_subelem = get_ie(multi_ap_ie + 4, + multi_ap_len - 4, + MULTI_AP_SUB_ELEM_TYPE); + if (multi_ap_subelem && multi_ap_subelem[1] == 1) { + multi_ap_value = multi_ap_subelem[2]; + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Multi-AP IE has missing or invalid Multi-AP subelement"); + return WLAN_STATUS_INVALID_IE; + } + } + + if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Multi-AP IE with unexpected value 0x%02x", + multi_ap_value); + + if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) { + if (hapd->conf->multi_ap & FRONTHAUL_BSS) + return WLAN_STATUS_SUCCESS; + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Non-Multi-AP STA tries to associate with backhaul-only BSS"); + return WLAN_STATUS_ASSOC_DENIED_UNSPEC; + } + + if (!(hapd->conf->multi_ap & BACKHAUL_BSS)) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Backhaul STA tries to associate with fronthaul-only BSS"); + + sta->flags |= WLAN_STA_MULTI_AP; + return WLAN_STATUS_SUCCESS; +} + static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) @@ -2466,6 +2821,11 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, resp = copy_supp_rates(hapd, sta, &elems); if (resp != WLAN_STATUS_SUCCESS) return resp; + + resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + #ifdef CONFIG_IEEE80211N resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities); if (resp != WLAN_STATUS_SUCCESS) @@ -2485,6 +2845,10 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; + resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation); + 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; @@ -2577,7 +2941,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "state machine"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg); res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + hapd->iface->freq, wpa_ie, wpa_ie_len, elems.mdie, elems.mdie_len, elems.owe_dh, elems.owe_dh_len); @@ -2669,6 +3035,37 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + hapd->conf->dpp_netaccesskey && sta->wpa_sm && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP && + elems.owe_dh) { + sta->dpp_pfs = dpp_pfs_init( + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!sta->dpp_pfs) { + wpa_printf(MSG_DEBUG, + "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + + if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } + + wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ? + sta->dpp_pfs->secret : NULL); + pfs_fail: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { @@ -2713,10 +3110,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_HS20 wpabuf_free(sta->hs20_ie); if (elems.hs20 && elems.hs20_len > 4) { + int release; + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, elems.hs20_len - 4); - } else + release = ((elems.hs20[4] >> 4) & 0x0f) + 1; + if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, + "HS 2.0: PMF not negotiated by release %d station " + MACSTR, release, MAC2STR(sta->addr)); + return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + } + } else { sta->hs20_ie = NULL; + } wpabuf_free(sta->roaming_consortium); if (elems.roaming_cons_sel) @@ -2747,6 +3154,35 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_MBO */ +#if defined(CONFIG_FILS) && defined(CONFIG_OCV) + if (wpa_auth_uses_ocv(sta->wpa_sm) && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (get_sta_tx_parameters(sta->wpa_sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } +#endif /* CONFIG_FILS && CONFIG_OCV */ + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, elems.supp_op_classes_len); @@ -2791,7 +3227,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, static int add_associated_sta(struct hostapd_data *hapd, - struct sta_info *sta) + struct sta_info *sta, int reassoc) { struct ieee80211_ht_capabilities ht_cap; struct ieee80211_vht_capabilities vht_cap; @@ -2807,14 +3243,36 @@ static int add_associated_sta(struct hostapd_data *hapd, * Skip this if the STA has already completed FT reassociation and the * TK has been configured since the TX/RX PN must not be reset to 0 for * the same key. + * + * FT-over-the-DS has a special case where the STA entry (and as such, + * the TK) has not yet been configured to the driver depending on which + * driver interface is used. For that case, allow add-STA operation to + * be used (instead of set-STA). This is needed to allow mac80211-based + * drivers to accept the STA parameter configuration. Since this is + * after a new FT-over-DS exchange, a new TK has been derived, so key + * reinstallation is not a concern for this case. */ + wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR + " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)", + MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg, + sta->ft_over_ds, reassoc, + !!(sta->flags & WLAN_STA_AUTHORIZED), + wpa_auth_sta_ft_tk_already_set(sta->wpa_sm), + wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)); + if (!sta->added_unassoc && (!(sta->flags & WLAN_STA_AUTHORIZED) || + (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) || (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) { hostapd_drv_sta_remove(hapd, sta->addr); wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); set = 0; + + /* Do not allow the FT-over-DS exception to be used more than + * once per authentication exchange to guarantee a new TK is + * used here */ + sta->ft_over_ds = 0; } #ifdef CONFIG_IEEE80211N @@ -2860,7 +3318,7 @@ static int add_associated_sta(struct hostapd_data *hapd, static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 status_code, int reassoc, - const u8 *ies, size_t ies_len) + const u8 *ies, size_t ies_len, int rssi) { int send_len; u8 *buf; @@ -2878,6 +3336,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) buflen += 150; #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (sta && sta->dpp_pfs) + buflen += 5 + sta->dpp_pfs->curve->prime_len; +#endif /* CONFIG_DPP2 */ buf = os_zalloc(buflen); if (!buf) { res = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -2905,6 +3367,16 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); +#ifdef CONFIG_MBO + if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && + rssi != 0) { + int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi; + + p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p, + delta); + } +#endif /* CONFIG_MBO */ + #ifdef CONFIG_IEEE80211R_AP if (sta && status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS @@ -2975,6 +3447,39 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FST */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + /* OWE Diffie-Hellman Parameter element */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ + WPA_PUT_LE16(p, sta->owe_group); + p += 2; + os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); + p += wpabuf_len(pub); + wpabuf_free(pub); + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) && + sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) { + os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie), + wpabuf_len(sta->dpp_pfs->ie)); + p += wpabuf_len(sta->dpp_pfs->ie); + } +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211AC if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); @@ -2996,6 +3501,9 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_WPS */ + if (sta && (sta->flags & WLAN_STA_MULTI_AP)) + p = hostapd_eid_multi_ap(hapd, p); + #ifdef CONFIG_P2P if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; @@ -3068,30 +3576,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_FILS */ -#ifdef CONFIG_OWE - if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && - sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && - wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { - struct wpabuf *pub; - - pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); - if (!pub) { - res = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto done; - } - /* OWE Diffie-Hellman Parameter element */ - *p++ = WLAN_EID_EXTENSION; /* Element ID */ - *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ - *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ - WPA_PUT_LE16(p, sta->owe_group); - p += 2; - os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); - p += wpabuf_len(pub); - send_len += 3 + 2 + wpabuf_len(pub); - wpabuf_free(pub); - } -#endif /* CONFIG_OWE */ - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); @@ -3173,7 +3657,7 @@ void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, sta->fils_pending_assoc_is_reassoc, sta->fils_pending_assoc_req, - sta->fils_pending_assoc_req_len); + sta->fils_pending_assoc_req_len, 0); os_free(sta->fils_pending_assoc_req); sta->fils_pending_assoc_req = NULL; sta->fils_pending_assoc_req_len = 0; @@ -3210,7 +3694,7 @@ void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, - int reassoc) + int reassoc, int rssi) { u16 capab_info, listen_interval, seq_ctrl, fc; u16 resp = WLAN_STATUS_SUCCESS, reply_res; @@ -3393,6 +3877,14 @@ static void handle_assoc(struct hostapd_data *hapd, resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } + + if (hapd->iconf->rssi_reject_assoc_rssi && rssi && + rssi < hapd->iconf->rssi_reject_assoc_rssi && + (sta->auth_rssi == 0 || + sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) { + resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS; + goto fail; + } #endif /* CONFIG_MBO */ /* @@ -3550,10 +4042,24 @@ static void handle_assoc(struct hostapd_data *hapd, * issues with processing other non-Data Class 3 frames during this * window. */ - if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta)) + if (resp == WLAN_STATUS_SUCCESS && sta && + add_associated_sta(hapd, sta, reassoc)) resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; #ifdef CONFIG_FILS + if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS && + eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) && + sta->fils_pending_assoc_req) { + /* Do not reschedule fils_hlp_timeout in case the station + * retransmits (Re)Association Request frame while waiting for + * the previously started FILS HLP wait, so that the timeout can + * be determined from the first pending attempt. */ + wpa_printf(MSG_DEBUG, + "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + os_free(tmp); + return; + } if (sta) { eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); os_free(sta->fils_pending_assoc_req); @@ -3578,7 +4084,7 @@ static void handle_assoc(struct hostapd_data *hapd, #endif /* CONFIG_FILS */ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, - left); + left, rssi); os_free(tmp); /* @@ -3718,28 +4224,6 @@ static void handle_beacon(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W - -static int hostapd_sa_query_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, - size_t len) -{ - const u8 *end; - - end = mgmt->u.action.u.sa_query_resp.trans_id + - WLAN_SA_QUERY_TR_ID_LEN; - if (((u8 *) mgmt) + len < end) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " - "frame (len=%lu)", (unsigned long) len); - 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; -} - - static int robust_action_frame(u8 category) { return category != WLAN_ACTION_PUBLIC && @@ -3825,7 +4309,8 @@ static int handle_action(struct hostapd_data *hapd, return 1; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: - return hostapd_sa_query_action(hapd, mgmt, len); + ieee802_11_sa_query_action(hapd, mgmt, len); + return 1; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: @@ -4020,17 +4505,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, switch (stype) { case WLAN_FC_STYPE_AUTH: wpa_printf(MSG_DEBUG, "mgmt::auth"); - handle_auth(hapd, mgmt, len); + handle_auth(hapd, mgmt, len, ssi_signal, 0); ret = 1; break; case WLAN_FC_STYPE_ASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); - handle_assoc(hapd, mgmt, len, 0); + handle_assoc(hapd, mgmt, len, 0, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_REASSOC_REQ: wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); - handle_assoc(hapd, mgmt, len, 1); + handle_assoc(hapd, mgmt, len, 1, ssi_signal); ret = 1; break; case WLAN_FC_STYPE_DISASSOC: @@ -4236,7 +4721,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd, sta->flags |= WLAN_STA_WDS; } - if (sta->flags & WLAN_STA_WDS) { + if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) { int ret; char ifname_wds[IFNAMSIZ + 1]; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 2f3b4da8e752..db7badcfffaf 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -59,6 +59,7 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_mu_edca_parameter_set(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, @@ -80,6 +81,8 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab); +u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper); 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, @@ -91,8 +94,8 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, struct sta_info *sta, u8 *eid); void ieee802_11_sa_query_action(struct hostapd_data *hapd, - const u8 *sa, const u8 action_type, - const u8 *trans_id); + const struct ieee80211_mgmt *mgmt, + size_t len); u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); @@ -120,6 +123,9 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len); u8 hostapd_mbo_ie_len(struct hostapd_data *hapd); +u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, + size_t len, int delta); + #else /* CONFIG_MBO */ static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, @@ -166,4 +172,9 @@ int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, char **identity, char **radius_cui, int is_probe_req); +int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx); + +void auth_sae_process_commit(void *eloop_ctx, void *user_ctx); + #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 5cb7fb1454f7..931d4d0659c3 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -289,6 +289,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_ACCEPT; }; + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + vlan_id = NULL; + /* Check whether ACL cache has an entry for this station */ res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, vlan_id, psk, @@ -516,7 +519,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); - int *untagged, *tagged, *notempty; query = hapd->acl_queries; prev = NULL; @@ -574,12 +576,10 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->acct_interim_interval = 0; } - notempty = &cache->vlan_id.notempty; - untagged = &cache->vlan_id.untagged; - tagged = cache->vlan_id.tagged; - *notempty = !!radius_msg_get_vlanid(msg, untagged, - MAX_NUM_TAGGED_VLAN, - tagged); + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) + cache->vlan_id.notempty = !!radius_msg_get_vlanid( + msg, &cache->vlan_id.untagged, + MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c index 1a8d46972985..072135863682 100644 --- a/src/ap/ieee802_11_he.c +++ b/src/ap/ieee802_11_he.c @@ -86,3 +86,34 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) return pos; } + + +u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_mu_edca_parameter_set *edca; + u8 *pos; + size_t i; + + pos = (u8 *) &hapd->iface->conf->he_mu_edca; + for (i = 0; i < sizeof(*edca); i++) { + if (pos[i]) + break; + } + if (i == sizeof(*edca)) + return eid; /* no MU EDCA Parameters configured */ + + pos = eid; + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(*edca); + *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS; + + edca = (struct ieee80211_he_mu_edca_parameter_set *) pos; + os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca)); + + wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element", + pos, sizeof(*edca)); + + pos += sizeof(*edca); + + return pos; +} diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 49e9bf8cc7c9..707381ffe709 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -10,10 +10,12 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/ocv.h" #include "hostapd.h" #include "sta_info.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wpa_auth.h" #include "ieee802_11.h" @@ -49,7 +51,12 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id) { - struct ieee80211_mgmt mgmt; +#ifdef CONFIG_OCV + struct sta_info *sta; +#endif /* CONFIG_OCV */ + struct ieee80211_mgmt *mgmt; + u8 *oci_ie = NULL; + u8 oci_ie_len = 0; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " @@ -57,19 +64,61 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", trans_id, WLAN_SA_QUERY_TR_ID_LEN); - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(mgmt.da, addr, ETH_ALEN); - os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); - mgmt.u.action.category = WLAN_ACTION_SA_QUERY; - mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; - os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, +#ifdef CONFIG_OCV + sta = ap_get_sta(hapd, addr); + if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Request"); + return; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for OCI element in SA Query Request"); + return; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(oci_ie); + return; + } + } +#endif /* CONFIG_OCV */ + + mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len); + if (!mgmt) { + wpa_printf(MSG_DEBUG, + "Failed to allocate buffer for SA Query Response frame"); + os_free(oci_ie); + return; + } + + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_SA_QUERY; + mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + end = mgmt->u.action.u.sa_query_req.variable; +#ifdef CONFIG_OCV + if (oci_ie_len > 0) { + os_memcpy(end, oci_ie, oci_ie_len); + end += oci_ie_len; + } +#endif /* CONFIG_OCV */ + if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0) wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); + + os_free(mgmt); + os_free(oci_ie); } @@ -77,7 +126,9 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, const u8 *sa, const u8 *trans_id) { struct sta_info *sta; - struct ieee80211_mgmt resp; + struct ieee80211_mgmt *resp; + u8 *oci_ie = NULL; + u8 oci_ie_len = 0; u8 *end; wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " @@ -92,30 +143,123 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, return; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Response"); + return; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for for OCI element in SA Query Response"); + return; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(oci_ie); + return; + } + } +#endif /* CONFIG_OCV */ + + resp = os_zalloc(sizeof(*resp) + oci_ie_len); + if (!resp) { + wpa_printf(MSG_DEBUG, + "Failed to allocate buffer for SA Query Response frame"); + os_free(oci_ie); + return; + } + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " MACSTR, MAC2STR(sa)); - os_memset(&resp, 0, sizeof(resp)); - resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(resp.da, sa, ETH_ALEN); - os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); - resp.u.action.category = WLAN_ACTION_SA_QUERY; - resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; - os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id, + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp->da, sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.action.category = WLAN_ACTION_SA_QUERY; + resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id, WLAN_SA_QUERY_TR_ID_LEN); - end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; - if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + end = resp->u.action.u.sa_query_req.variable; +#ifdef CONFIG_OCV + if (oci_ie_len > 0) { + os_memcpy(end, oci_ie, oci_ie_len); + end += oci_ie_len; + } +#endif /* CONFIG_OCV */ + if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0) wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); + + os_free(resp); + os_free(oci_ie); } -void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, - const u8 action_type, const u8 *trans_id) +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) { struct sta_info *sta; int i; + const u8 *sa = mgmt->sa; + const u8 action_type = mgmt->u.action.u.sa_query_resp.action; + const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id; + + if (((const u8 *) mgmt) + len < + mgmt->u.action.u.sa_query_resp.variable) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11: Too short SA Query Action frame (len=%lu)", + (unsigned long) len); + return; + } + + sta = ap_get_sta(hapd, sa); + +#ifdef CONFIG_OCV + if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct ieee802_11_elems elems; + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + size_t ies_len; + const u8 *ies; + + ies = mgmt->u.action.u.sa_query_resp.variable; + ies_len = len - (ies - (u8 *) mgmt); + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "SA Query: Failed to parse elements"); + return; + } + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in SA Query Action frame"); + return; + } + + if (get_sta_tx_parameters(sta->wpa_sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ if (action_type == WLAN_SA_QUERY_REQUEST) { ieee802_11_send_sa_query_resp(hapd, sa, trans_id); @@ -135,7 +279,6 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, /* MLME-SAQuery.confirm */ - sta = ap_get_sta(hapd, sa); if (sta == NULL || sta->sa_query_trans_id == NULL) { wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " "pending SA Query request found"); @@ -237,6 +380,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x01; #endif /* CONFIG_FILS */ break; + case 10: /* Bits 80-87 */ +#ifdef CONFIG_SAE + if (hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) { + int in_use = hostapd_sae_pw_id_in_use(hapd->conf); + + if (in_use) + *pos |= 0x02; /* Bit 81 - SAE Password + * Identifiers In Use */ + if (in_use == 2) + *pos |= 0x04; /* Bit 82 - SAE Password + * Identifiers Used Exclusively */ + } +#endif /* CONFIG_SAE */ + break; } } @@ -276,6 +434,12 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) len = 10; #endif /* CONFIG_FILS */ +#ifdef CONFIG_SAE + if (len < 11 && hapd->conf->wpa && + wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + hostapd_sae_pw_id_in_use(hapd->conf)) + len = 11; +#endif /* CONFIG_SAE */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -547,6 +711,22 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) #ifdef CONFIG_MBO +u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid, + size_t len, int delta) +{ + u8 mbo[4]; + + mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT; + mbo[1] = 2; + /* Delta RSSI */ + mbo[2] = delta; + /* Retry delay */ + mbo[3] = hapd->iconf->rssi_reject_assoc_timeout; + + return eid + mbo_add_ie(eid, len, mbo, 4); +} + + u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) { u8 mbo[9], *mbo_pos = mbo; @@ -752,3 +932,71 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) return pos; } + + +#ifdef CONFIG_OCV +int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx) +{ + int ht_40mhz = 0; + int vht_80p80 = 0; + int requested_bw; + + if (sta->ht_capabilities) + ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); + + if (sta->vht_operation) { + struct ieee80211_vht_operation *oper = sta->vht_operation; + + /* + * If a VHT Operation element was present, use it to determine + * the supported channel bandwidth. + */ + if (oper->vht_op_info_chwidth == 0) { + requested_bw = ht_40mhz ? 40 : 20; + } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) { + requested_bw = 80; + } else { + int diff; + + requested_bw = 160; + diff = abs((int) + oper->vht_op_info_chan_center_freq_seg0_idx - + (int) + oper->vht_op_info_chan_center_freq_seg1_idx); + vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx + != 0 && diff > 16; + } + } else if (sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capab; + int vht_chanwidth; + + capab = sta->vht_capabilities; + + /* + * If only the VHT Capabilities element is present (e.g., for + * normal clients), use it to determine the supported channel + * bandwidth. + */ + vht_chanwidth = capab->vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_MASK; + vht_80p80 = capab->vht_capabilities_info & + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + + /* TODO: Also take into account Extended NSS BW Support field */ + requested_bw = vht_chanwidth ? 160 : 80; + } else { + requested_bw = ht_40mhz ? 40 : 20; + } + + *bandwidth = requested_bw < ap_max_chanwidth ? + requested_bw : ap_max_chanwidth; + + *seg1_idx = 0; + if (ap_seg1_idx && vht_80p80) + *seg1_idx = ap_seg1_idx; + + return 0; +} +#endif /* CONFIG_OCV */ diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 8d0662078bcf..54ee080a43f0 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -357,6 +357,29 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, } +u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper) +{ + if (!vht_oper) { + os_free(sta->vht_operation); + sta->vht_operation = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (!sta->vht_operation) { + sta->vht_operation = + os_zalloc(sizeof(struct ieee80211_vht_operation)); + if (!sta->vht_operation) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + os_memcpy(sta->vht_operation, vht_oper, + sizeof(struct ieee80211_vht_operation)); + + return WLAN_STATUS_SUCCESS; +} + + u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ie, size_t len) { diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 185279f9e3ef..97f503f75cc3 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -682,9 +682,8 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, #ifdef CONFIG_HS20 if (hapd->conf->hs20) { - u8 ver = 1; /* Release 2 */ - if (HS20_VERSION > 0x10) - ver = 2; /* Release 3 */ + u8 ver = hapd->conf->hs20_release - 1; + if (!radius_msg_add_wfa( msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, &ver, 1)) { @@ -1237,6 +1236,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); + wpa_auth_set_ptk_rekey_timer(sta->wpa_sm); return; } #endif /* CONFIG_FILS */ @@ -1743,6 +1743,45 @@ ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) } +#ifndef CONFIG_NO_VLAN +static int ieee802_1x_update_vlan(struct radius_msg *msg, + struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct vlan_description vlan_desc; + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged, + MAX_NUM_TAGGED_VLAN, + vlan_desc.tagged); + + if (vlan_desc.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_desc.untagged, + vlan_desc.tagged[0] ? "+" : ""); + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + ap_sta_set_vlan(hapd, sta, &vlan_desc); + return -1; + } + + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && + !vlan_desc.notempty) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "authentication server did not include required VLAN ID in Access-Accept"); + return -1; + } + + return ap_sta_set_vlan(hapd, sta, &vlan_desc); +} +#endif /* CONFIG_NO_VLAN */ + + /** * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server * @msg: RADIUS response message @@ -1765,12 +1804,6 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); - struct vlan_description vlan_desc; -#ifndef CONFIG_NO_VLAN - int *untagged, *tagged, *notempty; -#endif /* CONFIG_NO_VLAN */ - - os_memset(&vlan_desc, 0, sizeof(vlan_desc)); sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); if (sm == NULL) { @@ -1835,56 +1868,21 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: #ifndef CONFIG_NO_VLAN - if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) { - notempty = &vlan_desc.notempty; - untagged = &vlan_desc.untagged; - tagged = vlan_desc.tagged; - *notempty = !!radius_msg_get_vlanid(msg, untagged, - MAX_NUM_TAGGED_VLAN, - tagged); - } - - if (vlan_desc.notempty && - !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { - sta->eapol_sm->authFail = TRUE; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d%s received from RADIUS server", - vlan_desc.untagged, - vlan_desc.tagged[0] ? "+" : ""); - os_memset(&vlan_desc, 0, sizeof(vlan_desc)); - ap_sta_set_vlan(hapd, sta, &vlan_desc); - break; - } - - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && - !vlan_desc.notempty) { - sta->eapol_sm->authFail = TRUE; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "authentication " - "server did not include required VLAN " - "ID in Access-Accept"); - break; - } -#endif /* CONFIG_NO_VLAN */ - - if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + ieee802_1x_update_vlan(msg, hapd, sta) < 0) break; -#ifndef CONFIG_NO_VLAN if (sta->vlan_id > 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } -#endif /* CONFIG_NO_VLAN */ if ((sta->flags & WLAN_STA_ASSOC) && ap_sta_bind_vlan(hapd, sta) < 0) break; +#endif /* CONFIG_NO_VLAN */ sta->session_timeout_set = !!session_timeout_set; os_get_reltime(&sta->session_timeout); @@ -2597,6 +2595,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, struct os_reltime diff; const char *name1; const char *name2; + char *identity_buf = NULL; if (sm == NULL) return 0; @@ -2712,6 +2711,14 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, /* dot1xAuthSessionStatsTable */ os_reltime_age(&sta->acct_session_start, &diff); + if (sm->eap && !sm->identity) { + const u8 *id; + size_t id_len; + + id = eap_get_identity(sm->eap, &id_len); + if (id) + identity_buf = dup_binstr(id, id_len); + } ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -2727,7 +2734,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, (unsigned int) diff.sec, - sm->identity); + sm->identity ? (char *) sm->identity : + (identity_buf ? identity_buf : "N/A")); + os_free(identity_buf); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c index b8fd5924b889..2b6f72726b64 100644 --- a/src/ap/neighbor_db.c +++ b/src/ap/neighbor_db.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "hostapd.h" +#include "ieee802_11.h" #include "neighbor_db.h" @@ -123,7 +124,7 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, } -void hostpad_free_neighbor_db(struct hostapd_data *hapd) +void hostapd_free_neighbor_db(struct hostapd_data *hapd) { struct hostapd_neighbor_entry *nr, *prev; @@ -134,3 +135,123 @@ void hostpad_free_neighbor_db(struct hostapd_data *hapd) os_free(nr); } } + + +#ifdef NEED_AP_MLME +static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd, + int ht, int vht) +{ + if (!ht && !vht) + return NR_CHAN_WIDTH_20; + if (!hapd->iconf->secondary_channel) + return NR_CHAN_WIDTH_20; + if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + return NR_CHAN_WIDTH_40; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ) + return NR_CHAN_WIDTH_80; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ) + return NR_CHAN_WIDTH_160; + if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) + return NR_CHAN_WIDTH_80P80; + return NR_CHAN_WIDTH_20; +} +#endif /* NEED_AP_MLME */ + + +void hostapd_neighbor_set_own_report(struct hostapd_data *hapd) +{ +#ifdef NEED_AP_MLME + u16 capab = hostapd_own_capab_info(hapd); + int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n; + int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; + struct wpa_ssid_value ssid; + u8 channel, op_class; + u8 center_freq1_idx = 0, center_freq2_idx = 0; + enum nr_chan_width width; + u32 bssid_info; + struct wpabuf *nr; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + bssid_info = 3; /* AP is reachable */ + bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */ + bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */ + + if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) + bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + + bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */ + + if (hapd->conf->wmm_enabled) { + bssid_info |= NEI_REP_BSSID_INFO_QOS; + + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) + bssid_info |= NEI_REP_BSSID_INFO_APSD; + } + + if (ht) { + bssid_info |= NEI_REP_BSSID_INFO_HT | + NEI_REP_BSSID_INFO_DELAYED_BA; + + /* VHT bit added in IEEE P802.11-REVmc/D4.3 */ + if (vht) + bssid_info |= NEI_REP_BSSID_INFO_VHT; + } + + /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ + + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return; + width = hostapd_get_nr_chan_width(hapd, ht, vht); + if (vht) { + center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; + if (width == NR_CHAN_WIDTH_80P80) + center_freq2_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; + } else if (ht) { + ieee80211_freq_to_chan(hapd->iface->freq + + 10 * hapd->iconf->secondary_channel, + ¢er_freq1_idx); + } + + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + + /* + * Neighbor Report element size = BSSID + BSSID info + op_class + chan + + * phy type + wide bandwidth channel subelement. + */ + nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5); + if (!nr) + return; + + wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN); + wpabuf_put_le32(nr, bssid_info); + wpabuf_put_u8(nr, op_class); + wpabuf_put_u8(nr, channel); + wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht)); + + /* + * Wide Bandwidth Channel subelement may be needed to allow the + * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0 + * Figure 9-301. + */ + wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); + wpabuf_put_u8(nr, 3); + wpabuf_put_u8(nr, width); + wpabuf_put_u8(nr, center_freq1_idx); + wpabuf_put_u8(nr, center_freq2_idx); + + hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, + hapd->iconf->civic, hapd->iconf->stationary_ap); + + wpabuf_free(nr); +#endif /* NEED_AP_MLME */ +} diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h index ba46d88435e5..9c8f4f2dd69d 100644 --- a/src/ap/neighbor_db.h +++ b/src/ap/neighbor_db.h @@ -17,8 +17,9 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, const struct wpabuf *civic, int stationary); +void hostapd_neighbor_set_own_report(struct hostapd_data *hapd); int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid); -void hostpad_free_neighbor_db(struct hostapd_data *hapd); +void hostapd_free_neighbor_db(struct hostapd_data *hapd); #endif /* NEIGHBOR_DB_H */ diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 56ed29c1c068..f2d5cd16e885 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -558,7 +558,7 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, void hostapd_clean_rrm(struct hostapd_data *hapd) { - hostpad_free_neighbor_db(hapd); + hostapd_free_neighbor_db(hapd); eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); hapd->lci_req_active = 0; eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 179cf43b60b1..4f9eae8477b6 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -13,6 +13,7 @@ #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "common/sae.h" +#include "common/dpp.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "p2p/p2p.h" @@ -166,7 +167,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) /* just in case */ ap_sta_set_authorized(hapd, sta, 0); - if (sta->flags & WLAN_STA_WDS) + if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); if (sta->ipaddr) @@ -328,6 +329,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->ht_capabilities); os_free(sta->vht_capabilities); + os_free(sta->vht_operation); hostapd_free_psk_list(sta->psk); os_free(sta->identity); os_free(sta->radius_cui); @@ -361,6 +363,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) crypto_ecdh_deinit(sta->owe_ecdh); #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + dpp_pfs_free(sta->dpp_pfs); + sta->dpp_pfs = NULL; +#endif /* CONFIG_DPP2 */ + os_free(sta->ext_capability); #ifdef CONFIG_WNM_AP @@ -500,6 +507,13 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; + if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) { + /* Cannot disassociate not-associated STA, so move + * directly to deauthentication. */ + sta->timeout_next = STA_DEAUTH; + deauth = 1; + } + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Timeout, sending %s info to STA " MACSTR, deauth ? "deauthentication" : "disassociation", @@ -798,6 +812,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); ieee802_1x_free_station(hapd, sta); + wpa_auth_sta_deinit(sta->wpa_sm); + sta->wpa_sm = NULL; sta->disassoc_reason = reason; sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; @@ -896,9 +912,6 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL; int old_vlan_id, vlan_id = 0, ret = 0; - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - vlan_desc = NULL; - /* Check if there is something to do */ if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) { /* This sta is lacking its own vif */ @@ -1165,6 +1178,32 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_IEEE80211W */ +const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct hostapd_wpa_psk *psk; + struct hostapd_ssid *ssid; + const u8 *pmk; + int pmk_len; + + ssid = &hapd->conf->ssid; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + if (!pmk || pmk_len != PMK_LEN) + return NULL; + + for (psk = ssid->wpa_psk; psk; psk = psk->next) + if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0) + break; + if (!psk) + return NULL; + if (!psk || !psk->keyid[0]) + return NULL; + + return psk->keyid; +} + + void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized) { @@ -1203,7 +1242,11 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, sta->addr, authorized, dev_addr); if (authorized) { + const char *keyid; + char keyid_buf[100]; char ip_addr[100]; + + keyid_buf[0] = '\0'; ip_addr[0] = '\0'; #ifdef CONFIG_P2P if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { @@ -1214,14 +1257,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_P2P */ - wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", - buf, ip_addr); + keyid = ap_sta_wpa_get_keyid(hapd, sta); + if (keyid) { + os_snprintf(keyid_buf, sizeof(keyid_buf), + " keyid=%s", keyid); + } + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s", + buf, ip_addr, keyid_buf); 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); + AP_STA_CONNECTED "%s%s%s", + buf, ip_addr, keyid_buf); } else { wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 9cac6f157f68..ece0c60abd36 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -36,6 +36,7 @@ #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) #define WLAN_STA_VENDOR_VHT BIT(21) #define WLAN_STA_PENDING_FILS_ERP BIT(22) +#define WLAN_STA_MULTI_AP BIT(23) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -117,6 +118,7 @@ struct sta_info { unsigned int power_capab:1; unsigned int agreed_to_steer:1; unsigned int hs20_t_c_filtering:1; + unsigned int ft_over_ds:1; u16 auth_alg; @@ -162,6 +164,7 @@ struct sta_info { struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; + struct ieee80211_vht_operation *vht_operation; u8 vht_opmode; #ifdef CONFIG_IEEE80211W @@ -215,6 +218,7 @@ struct sta_info { u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise, * enum mbo_cellular_capa values */ struct mbo_non_pref_chan_info *non_pref_chan; + int auth_rssi; /* Last Authentication frame RSSI */ #endif /* CONFIG_MBO */ u8 *supp_op_classes; /* Supported Operating Classes element, if @@ -261,6 +265,10 @@ struct sta_info { u8 *ext_capability; char *ifname_wds; /* WDS ifname, if in use */ +#ifdef CONFIG_DPP2 + struct dpp_pfs *dpp_pfs; +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_TESTING_OPTIONS enum wpa_alg last_tk_alg; int last_tk_key_idx; @@ -320,6 +328,8 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); +const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd, + struct sta_info *sta); void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, const u8 *addr, u16 reason); diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c index aa42335b96a1..19aa3c649a5d 100644 --- a/src/ap/vlan_full.c +++ b/src/ap/vlan_full.c @@ -16,6 +16,7 @@ #include "utils/common.h" #include "drivers/priv_netlink.h" +#include "drivers/linux_ioctl.h" #include "common/linux_bridge.h" #include "common/linux_vlan.h" #include "utils/eloop.h" @@ -143,6 +144,9 @@ static int br_delif(const char *br_name, const char *if_name) return -1; } + if (linux_br_del_if(fd, br_name, if_name) == 0) + goto done; + if_index = if_nametoindex(if_name); if (if_index == 0) { @@ -168,6 +172,7 @@ static int br_delif(const char *br_name, const char *if_name) return -1; } +done: close(fd); return 0; } @@ -194,6 +199,14 @@ static int br_addif(const char *br_name, const char *if_name) return -1; } + if (linux_br_add_if(fd, br_name, if_name) == 0) + goto done; + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + if_index = if_nametoindex(if_name); if (if_index == 0) { @@ -224,6 +237,7 @@ static int br_addif(const char *br_name, const char *if_name) return -1; } +done: close(fd); return 0; } @@ -241,6 +255,9 @@ static int br_delbr(const char *br_name) return -1; } + if (linux_br_del(fd, br_name) == 0) + goto done; + arg[0] = BRCTL_DEL_BRIDGE; arg[1] = (unsigned long) br_name; @@ -252,6 +269,7 @@ static int br_delbr(const char *br_name) return -1; } +done: close(fd); return 0; } @@ -277,11 +295,19 @@ static int br_addbr(const char *br_name) return -1; } + if (linux_br_add(fd, br_name) == 0) + goto done; + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } + arg[0] = BRCTL_ADD_BRIDGE; arg[1] = (unsigned long) br_name; if (ioctl(fd, SIOCGIFBR, arg) < 0) { - if (errno == EEXIST) { + if (errno == EEXIST) { /* The bridge is already added. */ close(fd); return 1; @@ -294,6 +320,7 @@ static int br_addbr(const char *br_name) } } +done: /* Decrease forwarding delay to avoid EAPOL timeouts. */ os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); @@ -363,12 +390,18 @@ static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, { char vlan_ifname[IFNAMSIZ]; int clean; + int ret; if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", - tagged_interface, vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); else - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", + vid); + if (ret >= (int) sizeof(vlan_ifname)) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan_ifname); clean = 0; ifconfig_up(tagged_interface); @@ -384,19 +417,28 @@ static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, } -static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid) +static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, + struct hostapd_vlan *vlan, int vid) { char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int ret; - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, IFNAMSIZ, "%s%d", - hapd->conf->vlan_bridge, vid); + if (vlan->bridge[0]) { + os_strlcpy(br_name, vlan->bridge, IFNAMSIZ); + ret = 0; + } else if (hapd->conf->vlan_bridge[0]) { + ret = os_snprintf(br_name, IFNAMSIZ, "%s%d", + hapd->conf->vlan_bridge, vid); } else if (tagged_interface) { - os_snprintf(br_name, IFNAMSIZ, "br%s.%d", - tagged_interface, vid); + ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d", + tagged_interface, vid); } else { - os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); + ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); } + if (ret >= IFNAMSIZ) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + br_name); } @@ -445,7 +487,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd) !br_addif(hapd->conf->bridge, ifname)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { - vlan_bridge_name(br_name, hapd, untagged); + vlan_bridge_name(br_name, hapd, vlan, untagged); vlan_get_bridge(br_name, hapd, untagged); @@ -458,7 +500,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd) tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || (i > 0 && tagged[i] == tagged[i - 1])) continue; - vlan_bridge_name(br_name, hapd, tagged[i]); + vlan_bridge_name(br_name, hapd, vlan, tagged[i]); vlan_get_bridge(br_name, hapd, tagged[i]); vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, ifname, br_name, tagged[i], hapd); @@ -474,12 +516,19 @@ static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, { char vlan_ifname[IFNAMSIZ]; int clean; + int ret; if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", - tagged_interface, vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); else - os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", + vid); + if (ret >= (int) sizeof(vlan_ifname)) + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan_ifname); + clean = dyn_iface_put(hapd, vlan_ifname); @@ -543,7 +592,7 @@ void vlan_dellink(const char *ifname, struct hostapd_data *hapd) tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || (i > 0 && tagged[i] == tagged[i - 1])) continue; - vlan_bridge_name(br_name, hapd, tagged[i]); + vlan_bridge_name(br_name, hapd, vlan, tagged[i]); vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, ifname, br_name, tagged[i], hapd); vlan_put_bridge(br_name, hapd, tagged[i]); @@ -555,7 +604,7 @@ void vlan_dellink(const char *ifname, struct hostapd_data *hapd) (vlan->clean & DVLAN_CLEAN_WLAN_PORT)) br_delif(hapd->conf->bridge, ifname); } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { - vlan_bridge_name(br_name, hapd, untagged); + vlan_bridge_name(br_name, hapd, vlan, untagged); if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) br_delif(br_name, vlan->ifname); diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 01fecee026a1..e293a003303f 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -187,6 +187,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, { struct hostapd_vlan *n; char ifname[IFNAMSIZ + 1], *pos; + int ret; if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; @@ -208,8 +209,13 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, n->vlan_desc = *vlan_desc; n->dynamic_vlan = 1; - os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, - pos); + ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", + ifname, vlan_id, pos); + if (os_snprintf_error(sizeof(n->ifname), ret)) { + os_free(n); + return NULL; + } + os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge)); n->next = hapd->conf->vlan; hapd->conf->vlan = n; diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 1e8f58b4d07e..27c69d34a7ca 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/ocv.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ap_config.h" @@ -54,8 +55,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, size_t gtk_elem_len = 0; size_t igtk_elem_len = 0; struct wnm_sleep_element wnmsleep_ie; - u8 *wnmtfs_ie; - u8 wnmsleep_ie_len; + u8 *wnmtfs_ie, *oci_ie; + u8 wnmsleep_ie_len, oci_ie_len; u16 wnmtfs_ie_len; u8 *pos; struct sta_info *sta; @@ -88,10 +89,42 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, wnmtfs_ie = NULL; } + oci_ie = NULL; + oci_ie_len = 0; +#ifdef CONFIG_OCV + if (action_type == WNM_SLEEP_MODE_EXIT && + wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); + os_free(wnmtfs_ie); + return -1; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame"); + os_free(wnmtfs_ie); + return -1; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(wnmtfs_ie); + os_free(oci_ie); + return -1; + } + } +#endif /* CONFIG_OCV */ + #define MAX_GTK_SUBELEM_LEN 45 #define MAX_IGTK_SUBELEM_LEN 26 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + - MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN + + oci_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); @@ -134,11 +167,18 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); /* copy TFS IE here */ pos += wnmsleep_ie_len; - if (wnmtfs_ie) + if (wnmtfs_ie) { os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + pos += wnmtfs_ie_len; + } +#ifdef CONFIG_OCV + /* copy OCV OCI here */ + if (oci_ie_len > 0) + os_memcpy(pos, oci_ie, oci_ie_len); +#endif /* CONFIG_OCV */ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + - igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len; /* In driver, response frame should be forced to sent when STA is in * PS mode */ @@ -185,6 +225,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #undef MAX_IGTK_SUBELEM_LEN fail: os_free(wnmtfs_ie); + os_free(oci_ie); os_free(mgmt); return res; } @@ -201,6 +242,11 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, u8 *tfsreq_ie_start = NULL; u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; +#ifdef CONFIG_OCV + struct sta_info *sta; + const u8 *oci_ie = NULL; + u8 oci_ie_len = 0; +#endif /* CONFIG_OCV */ if (!hapd->conf->wnm_sleep_mode) { wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from " @@ -209,6 +255,13 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, return; } + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short WNM-Sleep Mode Request from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *pos++; while (pos + 1 < frm + len) { u8 ie_len = pos[1]; @@ -221,6 +274,12 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, if (!tfsreq_ie_start) tfsreq_ie_start = (u8 *) pos; tfsreq_ie_end = (u8 *) pos; +#ifdef CONFIG_OCV + } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && + pos[2] == WLAN_EID_EXT_OCV_OCI) { + oci_ie = pos + 3; + oci_ie_len = ie_len - 1; +#endif /* CONFIG_OCV */ } else wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", *pos); @@ -232,6 +291,27 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, return; } +#ifdef CONFIG_OCV + sta = ap_get_sta(hapd, addr); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && + sta && wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + + if (hostapd_drv_channel_info(hapd, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); + return; + } + + if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && tfsreq_ie_start && tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) { diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 34969e79e46f..f2e028c1599e 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-2018, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +13,7 @@ #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" +#include "common/ocv.h" #include "crypto/aes.h" #include "crypto/aes_wrap.h" #include "crypto/aes_siv.h" @@ -22,6 +23,7 @@ #include "crypto/sha384.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" +#include "drivers/driver.h" #include "ap_config.h" #include "ieee802_11.h" #include "wpa_auth.h" @@ -112,12 +114,13 @@ 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 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { if (wpa_auth->cb->get_psk == NULL) return NULL; return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, - prev_psk, psk_len); + prev_psk, psk_len, vlan_id); } @@ -238,6 +241,26 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_OCV +static int wpa_channel_info(struct wpa_authenticator *wpa_auth, + struct wpa_channel_info *ci) +{ + if (!wpa_auth->cb->channel_info) + return -1; + return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); +} +#endif /* CONFIG_OCV */ + + +static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth, + const u8 *addr, int vlan_id) +{ + if (!wpa_auth->cb->update_vlan) + return -1; + return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id); +} + + static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { struct wpa_authenticator *wpa_auth = eloop_ctx; @@ -297,6 +320,19 @@ static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) } +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm) +{ + if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) { + wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for " + MACSTR " (%d seconds)", MAC2STR(sm->addr), + sm->wpa_auth->conf.wpa_ptk_rekey); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0, + wpa_rekey_ptk, sm->wpa_auth, sm); + } +} + + static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { if (sm->pmksa == ctx) @@ -332,6 +368,10 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, wpa_get_ntp_timestamp(buf + ETH_ALEN); ptr = (unsigned long) group; os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); +#ifdef TEST_FUZZ + os_memset(buf + ETH_ALEN, 0xab, 8); + os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr)); +#endif /* TEST_FUZZ */ if (random_get_bytes(rkey, sizeof(rkey)) < 0) return -1; @@ -669,7 +709,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); - os_free(sm); +#ifdef CONFIG_DPP2 + wpabuf_clear_free(sm->dpp_z); +#endif /* CONFIG_DPP2 */ + bin_clear_free(sm, sizeof(*sm)); } @@ -835,13 +878,15 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, int ok = 0; const u8 *pmk = NULL; size_t pmk_len; + int vlan_id = 0; os_memset(&PTK, 0, sizeof(PTK)); for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk, &pmk_len); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; #ifdef CONFIG_IEEE80211R_AP @@ -860,6 +905,10 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, data, data_len) == 0) { + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } @@ -878,6 +927,11 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, wpa_printf(MSG_DEBUG, "WPA: Earlier SNonce resulted in matching MIC"); sm->alt_snonce_valid = 0; + + if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0) + return -1; + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); sm->PTK_valid = TRUE; @@ -1202,6 +1256,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ return; } #ifdef CONFIG_FILS @@ -1210,9 +1269,17 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, &key_data_length) < 0) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ return; } #endif /* CONFIG_FILS */ +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; @@ -1317,6 +1384,9 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); pos = data + ETH_ALEN + WPA_NONCE_LEN; wpa_get_ntp_timestamp(pos); +#ifdef TEST_FUZZ + os_memset(pos, 0xef, 8); +#endif /* TEST_FUZZ */ pos += 8; if (random_get_bytes(pos, gtk_len) < 0) ret = -1; @@ -1596,6 +1666,9 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, timeout_ms = eapol_key_timeout_no_retrans; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; +#ifdef TEST_FUZZ + timeout_ms = 1; +#endif /* TEST_FUZZ */ wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " "counter %u)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, @@ -1670,6 +1743,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) case WPA_DEAUTH: case WPA_DISASSOC: sm->DeauthenticationRequest = TRUE; +#ifdef CONFIG_IEEE80211R_AP + os_memset(sm->PMK, 0, sizeof(sm->PMK)); + sm->pmk_len = 0; + os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + sm->xxkey_len = 0; + os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); + sm->pmk_r1_len = 0; +#endif /* CONFIG_IEEE80211R_AP */ break; case WPA_REAUTH: case WPA_REAUTH_EAPOL: @@ -1710,6 +1791,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; + wpa_auth_set_ptk_rekey_timer(sm); return 0; #else /* CONFIG_IEEE80211R_AP */ break; @@ -1986,7 +2068,7 @@ SM_STATE(WPA_PTK, INITPSK) SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, - &psk_len); + &psk_len, NULL); if (psk) { os_memcpy(sm->PMK, psk, psk_len); sm->pmk_len = psk_len; @@ -2000,6 +2082,10 @@ SM_STATE(WPA_PTK, INITPSK) wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); sm->pmk_len = sm->pmksa->pmk_len; +#ifdef CONFIG_IEEE80211R_AP + os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->xxkey_len = sm->pmksa->pmk_len; +#endif /* CONFIG_IEEE80211R_AP */ } #endif /* CONFIG_SAE */ sm->req_replay_counter_used = 0; @@ -2060,6 +2146,29 @@ SM_STATE(WPA_PTK, PTKSTART) wpa_printf(MSG_DEBUG, "RSN: No KCK available to derive PMKID for message 1/4"); pmkid = NULL; +#ifdef CONFIG_FILS + } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + if (sm->pmkid_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from FILS/ERP", + sm->pmkid, PMKID_LEN); + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmkid, PMKID_LEN); + } else { + /* No PMKID available */ + wpa_printf(MSG_DEBUG, + "RSN: No FILS/ERP PMKID available for message 1/4"); + pmkid = NULL; + } +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP + } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) && + sm->ft_completed) { + wpa_printf(MSG_DEBUG, + "FT: No PMKID in message 1/4 when using FT protocol"); + pmkid = NULL; + pmkid_len = 0; +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { if (sm->pmkid_set) { @@ -2098,14 +2207,36 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk) { + const u8 *z = NULL; + size_t z_len = 0; + #ifdef CONFIG_IEEE80211R_AP - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - return wpa_auth_derive_ptk_ft(sm, pmk, ptk); + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (sm->ft_completed) { + u8 ptk_name[WPA_PMK_NAME_LEN]; + + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, + sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, + sm->pmk_r1_name, + ptk, ptk_name, + sm->wpa_key_mgmt, + sm->pairwise); + } + return wpa_auth_derive_ptk_ft(sm, ptk); + } #endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_DPP2 + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { + z = wpabuf_head(sm->dpp_z); + z_len = wpabuf_len(sm->dpp_z); + } +#endif /* CONFIG_DPP2 */ + 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); + ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len); } @@ -2155,6 +2286,16 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, pmk_r0_name, WPA_PMK_NAME_LEN); wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); os_memset(fils_ft, 0, sizeof(fils_ft)); + + res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder, + sm->addr, sm->pmk_r1_name, + use_sha384); + os_memset(pmk_r0, 0, PMK_LEN_MAX); + if (res < 0) + return -1; + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + sm->pmk_r1_name_valid = 1; } #endif /* CONFIG_IEEE80211R_AP */ @@ -2559,6 +2700,27 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, wpabuf_put(plain, tmp2 - tmp); *len = (u8 *) wpabuf_put(plain, 0) - len - 1; + +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "FILS: Failed to get channel info for OCI element"); + wpabuf_free(plain); + return NULL; + } + + pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN); + if (ocv_insert_extended_oci(&ci, pos) < 0) { + wpabuf_free(plain); + return NULL; + } + } +#endif /* CONFIG_OCV */ + return plain; } @@ -2624,6 +2786,21 @@ u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, #endif /* CONFIG_FILS */ +#ifdef CONFIG_OCV +int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + + if (!wpa_auth->cb->get_sta_tx_params) + return -1; + return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr, + ap_max_chanwidth, ap_seg1_idx, + bandwidth, seg1_idx); +} +#endif /* CONFIG_OCV */ + + SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { struct wpa_authenticator *wpa_auth = sm->wpa_auth; @@ -2638,6 +2815,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; struct wpa_eapol_ie_parse kde; + int vlan_id = 0; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; @@ -2653,7 +2831,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk, &pmk_len); + sm->p2p_dev_addr, pmk, &pmk_len, + &vlan_id); if (pmk == NULL) break; psk_found = 1; @@ -2668,6 +2847,12 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) pmk_len = sm->pmk_len; } + if ((!pmk || !pmk_len) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache"); + pmk = sm->pmksa->pmk; + pmk_len = sm->pmksa->pmk_len; + } + if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) break; @@ -2675,6 +2860,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { + if (sm->PMK != pmk) { + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + } ok = 1; break; } @@ -2746,6 +2935,32 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) WLAN_REASON_PREV_AUTH_NOT_VALID); return; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(wpa_auth, &ci) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Failed to get channel info to validate received OCI in EAPOL-Key 2/4"); + return; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { wpa_sta_disconnect(wpa_auth, sm->addr, @@ -2794,6 +3009,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } #endif /* CONFIG_IEEE80211R_AP */ + if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); @@ -2883,6 +3105,36 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) #endif /* CONFIG_IEEE80211W */ +static int ocv_oci_len(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) + return OCV_OCI_KDE_LEN; +#endif /* CONFIG_OCV */ + return 0; +} + +static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos) +{ +#ifdef CONFIG_OCV + struct wpa_channel_info ci; + + if (!wpa_auth_uses_ocv(sm)) + return 0; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element"); + return -1; + } + + return ocv_insert_oci_kde(&ci, argpos); +#else /* CONFIG_OCV */ + return 0; +#endif /* CONFIG_OCV */ +} + + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; @@ -2966,7 +3218,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) } } - kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R_AP @@ -3011,6 +3263,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) gtk, gtk_len); } pos = ieee80211w_kde_add(sm, pos); + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde); + return; + } #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { @@ -3094,12 +3350,7 @@ SM_STATE(WPA_PTK, PTKINITDONE) /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; - if (sm->wpa_auth->conf.wpa_ptk_rekey) { - eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); - eloop_register_timeout(sm->wpa_auth->conf. - wpa_ptk_rekey, 0, wpa_rekey_ptk, - sm->wpa_auth, sm); - } + wpa_auth_set_ptk_rekey_timer(sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || @@ -3199,7 +3450,7 @@ SM_STEP(WPA_PTK) break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, - NULL, NULL)) { + NULL, NULL, NULL)) { SM_ENTER(WPA_PTK, PTKSTART); #ifdef CONFIG_SAE } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { @@ -3322,7 +3573,7 @@ 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); + ieee80211w_kde_len(sm) + ocv_oci_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return; @@ -3333,6 +3584,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde_buf); + return; + } kde_len = pos - kde; } else { kde = gtk; @@ -3353,8 +3608,67 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { +#ifdef CONFIG_OCV + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + const u8 *key_data, *mic; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; + size_t mic_len; + u16 key_data_length; +#endif /* CONFIG_OCV */ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; + +#ifdef CONFIG_OCV + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (struct wpa_eapol_key *) (hdr + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + return; + + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key group msg 2/2 with invalid Key Data contents"); + return; + } + + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(wpa_auth, &ci) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2"); + return; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (sm->GUpdateStationKeys) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; @@ -3963,6 +4277,15 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm) } +const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len) +{ + if (!sm) + return NULL; + *len = sm->pmk_len; + return sm->PMK; +} + + int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) { if (sm == NULL) @@ -4575,9 +4898,37 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, *fils_kek_len = sm->PTK.kek_len; } + +void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *pmkid) +{ + os_memcpy(sm->PMK, pmk, pmk_len); + sm->pmk_len = pmk_len; + os_memcpy(sm->pmkid, pmkid, PMKID_LEN); + sm->pmkid_set = 1; +} + #endif /* CONFIG_FILS */ +void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg) +{ + if (sm) + sm->auth_alg = auth_alg; +} + + +#ifdef CONFIG_DPP2 +void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z) +{ + if (sm) { + wpabuf_clear_free(sm->dpp_z); + sm->dpp_z = z ? wpabuf_dup(z) : NULL; + } +} +#endif /* CONFIG_DPP2 */ + + #ifdef CONFIG_TESTING_OPTIONS int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, @@ -4666,7 +5017,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, } } - kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; #ifdef CONFIG_IEEE80211R_AP @@ -4715,6 +5066,10 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm, os_memset(opos, 0, 6); /* clear PN */ } #endif /* CONFIG_IEEE80211W */ + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde); + return -1; + } #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { @@ -4796,7 +5151,7 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, gtk = gsm->GTK[gsm->GN - 1]; if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + - ieee80211w_kde_len(sm); + ieee80211w_kde_len(sm) + ocv_oci_len(sm); kde_buf = os_malloc(kde_len); if (kde_buf == NULL) return -1; @@ -4816,6 +5171,10 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, os_memset(opos, 0, 6); /* clear PN */ } #endif /* CONFIG_IEEE80211W */ + if (ocv_oci_add(sm, &pos) < 0) { + os_free(kde_buf); + return -1; + } kde_len = pos - kde; } else { kde = gtk; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index fad5536f740e..df1e17a003f8 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -145,6 +145,7 @@ struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; struct ft_remote_seq; +struct wpa_channel_info; struct ft_remote_r0kh { @@ -191,6 +192,9 @@ struct wpa_auth_config { int group_mgmt_cipher; int sae_require_mfp; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + int ocv; /* Operating Channel Validation */ +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP u8 ssid[SSID_MAX_LEN]; size_t ssid_len; @@ -250,7 +254,8 @@ struct wpa_auth_callbacks { 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 *p2p_dev_addr, - const u8 *prev_psk, size_t *psk_len); + const u8 *prev_psk, size_t *psk_len, + int *vlan_id); 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); @@ -265,6 +270,11 @@ struct wpa_auth_callbacks { size_t data_len); int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, size_t data_len); + int (*channel_info)(void *ctx, struct wpa_channel_info *ci); + int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id); + int (*get_sta_tx_params)(void *ctx, const u8 *addr, + int ap_max_chanwidth, int ap_seg1_idx, + int *bandwidth, int *seg1_idx); #ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*set_vlan)(void *ctx, const u8 *sta_addr, @@ -308,7 +318,7 @@ enum { }; int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + struct wpa_state_machine *sm, int freq, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len, const u8 *owe_dh, size_t owe_dh_len); @@ -316,6 +326,8 @@ 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); +void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv); +int wpa_auth_uses_ocv(struct wpa_state_machine *sm); struct wpa_state_machine * wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr); @@ -339,6 +351,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); int wpa_auth_pairwise_set(struct wpa_state_machine *sm); int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); @@ -449,14 +462,21 @@ const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len); +int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth, + int ap_seg1_idx, int *bandwidth, int *seg1_idx); + int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, u8 *buf, size_t len); void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, u8 *fils_anonce, u8 *fils_snonce, u8 *fils_kek, size_t *fils_kek_len); +void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *pmkid); u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 *pos, size_t max_len, const u8 *req_ies, size_t req_ies_len); +void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg); +void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z); int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, void (*cb)(void *ctx1, void *ctx2), @@ -468,5 +488,6 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, void (*cb)(void *ctx1, void *ctx2), void *ctx1, void *ctx2); int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); +void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm); #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index f6792e00f32d..ac16199a6006 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -13,6 +13,8 @@ #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" +#include "drivers/driver.h" #include "crypto/aes.h" #include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" @@ -64,7 +66,7 @@ struct tlv_list { * Returns: 0 on success, -1 on error */ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, - const u8 *enc, const size_t enc_len, + const u8 *enc, size_t enc_len, const u8 *auth, const size_t auth_len, const u8 *src_addr, u8 type, u8 **plain, size_t *plain_size) @@ -72,7 +74,11 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, const u8 *ad[3] = { src_addr, auth, &type }; size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", + MAC2STR(src_addr), type); wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); if (!key) { /* skip decryption */ *plain = os_memdup(enc, enc_len); @@ -95,8 +101,18 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, goto err; if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, - *plain) < 0) - goto err; + *plain) < 0) { + if (enc_len < AES_BLOCK_SIZE + 2) + goto err; + + /* Try to work around Ethernet devices that add extra + * two octet padding even if the frame is longer than + * the minimum Ethernet frame. */ + enc_len -= 2; + if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, + *plain) < 0) + goto err; + } *plain_size = enc_len - AES_BLOCK_SIZE; wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs", @@ -461,9 +477,12 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, const u8 *ad[3] = { src_addr, auth, &type }; size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u", + MAC2STR(src_addr), type); wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message", plain, plain_len); wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len); + wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len); if (!key) { /* encryption not needed, return plaintext as packet */ @@ -473,6 +492,8 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message"); return -1; } + wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", + enc, plain_len + AES_BLOCK_SIZE); return 0; } @@ -501,9 +522,10 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, const u8 *src_addr, u8 type, u8 **packet, size_t *packet_len) { - u8 *plain = NULL, *auth = NULL, *pos; + u8 *plain = NULL, *auth = NULL, *pos, *tmp; size_t plain_len = 0, auth_len = 0; int ret = -1; + size_t pad_len = 0; *packet = NULL; if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0) @@ -515,6 +537,28 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, *packet_len = sizeof(u16) + auth_len + plain_len; if (key) *packet_len += AES_BLOCK_SIZE; +#define RRB_MIN_MSG_LEN 64 + if (*packet_len < RRB_MIN_MSG_LEN) { + pad_len = RRB_MIN_MSG_LEN - *packet_len; + if (pad_len < sizeof(struct ft_rrb_tlv)) + pad_len = sizeof(struct ft_rrb_tlv); + wpa_printf(MSG_DEBUG, + "FT: Pad message to minimum Ethernet frame length (%d --> %d)", + (int) *packet_len, (int) (*packet_len + pad_len)); + *packet_len += pad_len; + tmp = os_realloc(auth, auth_len + pad_len); + if (!tmp) + goto out; + auth = tmp; + pos = auth + auth_len; + WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY); + pos += 2; + WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv)); + pos += 2; + os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv)); + auth_len += pad_len; + + } *packet = os_zalloc(*packet_len); if (!*packet) goto out; @@ -527,6 +571,7 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth, auth_len, src_addr, type, pos) < 0) goto out; + wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len); ret = 0; @@ -594,8 +639,8 @@ static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, { if (!wpa_auth->cb->send_oui) return -1; - wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR, - oui_suffix, MAC2STR(dst)); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)", + oui_suffix, MAC2STR(dst), (unsigned int) data_len); return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data, data_len); } @@ -618,7 +663,7 @@ static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth, if (wpa_auth->cb->get_psk == NULL) return NULL; return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, - prev_psk, NULL); + prev_psk, NULL, NULL); } @@ -727,6 +772,17 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, } +#ifdef CONFIG_OCV +static int wpa_channel_info(struct wpa_authenticator *wpa_auth, + struct wpa_channel_info *ci) +{ + if (!wpa_auth->cb->channel_info) + return -1; + return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci); +} +#endif /* CONFIG_OCV */ + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -894,6 +950,8 @@ wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, goto err; } + wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR, + MAC2STR(src_addr)); item = os_zalloc(sizeof(*item)); if (!item) goto err; @@ -2016,8 +2074,7 @@ int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, } -int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk) +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk) { u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? @@ -2373,10 +2430,24 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, end = pos + max_len; - if (auth_alg == WLAN_AUTH_FT) { + if (auth_alg == WLAN_AUTH_FT || + ((auth_alg == WLAN_AUTH_FILS_SK || + auth_alg == WLAN_AUTH_FILS_SK_PFS || + auth_alg == WLAN_AUTH_FILS_PK) && + (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)))) { + if (!sm->pmk_r1_name_valid) { + wpa_printf(MSG_ERROR, + "FT: PMKR1Name is not valid for Assoc Resp RSNE"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); /* * RSN (only present if this is a Reassociation Response and - * part of a fast BSS transition) + * part of a fast BSS transition; or if this is a + * (Re)Association Response frame during an FT initial mobility + * domain association using FILS) */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) @@ -2430,6 +2501,35 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, os_free(igtk); } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + u8 *nbuf, *ocipos; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element"); + os_free(subelem); + return NULL; + } + + subelem_len += 2 + OCV_OCI_LEN; + nbuf = os_realloc(subelem, subelem_len); + if (!nbuf) { + os_free(subelem); + return NULL; + } + subelem = nbuf; + + ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN; + *ocipos++ = FTIE_SUBELEM_OCI; + *ocipos++ = OCV_OCI_LEN; + if (ocv_insert_oci(&ci, &ocipos) < 0) { + os_free(subelem); + return NULL; + } + } +#endif /* CONFIG_OCV */ } else { r0kh_id = conf->r0_key_holder; r0kh_id_len = conf->r0_key_holder_len; @@ -2596,6 +2696,8 @@ static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm, os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN); if (out_pairwise) *out_pairwise = pairwise; + os_memcpy(sm->PMK, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; if (out_vlan && wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) { wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " @@ -2881,6 +2983,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len); + sm->pmk_r1_len = pmk_r1_len; if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " @@ -3178,6 +3282,32 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_FTIE; } +#ifdef CONFIG_OCV + if (wpa_auth_uses_ocv(sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_channel_info(sm->wpa_auth, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in (Re)Assoc Request"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (get_sta_tx_parameters(sm, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + } +#endif /* CONFIG_OCV */ + return WLAN_STATUS_SUCCESS; } @@ -4303,6 +4433,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " MACSTR, MAC2STR(src_addr)); wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix); + wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len); if (is_multicast_ether_addr(src_addr)) { wpa_printf(MSG_DEBUG, @@ -4331,8 +4462,10 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, } auth = data + sizeof(u16); + wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen); enc = data + sizeof(u16) + alen; elen = data_len - sizeof(u16) - alen; + wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen); switch (oui_suffix) { case FT_PACKET_R0KH_R1KH_PULL: diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 812740301c8b..45172c69a9fa 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -27,6 +27,7 @@ #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "ieee802_11.h" #include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" @@ -55,6 +56,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; +#ifdef CONFIG_OCV + wconf->ocv = conf->ocv; +#endif /* CONFIG_OCV */ wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; @@ -242,12 +246,15 @@ 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, size_t *psk_len) + const u8 *prev_psk, size_t *psk_len, + int *vlan_id) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; + if (vlan_id) + *vlan_id = 0; if (psk_len) *psk_len = PMK_LEN; @@ -283,7 +290,8 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, } #endif /* CONFIG_OWE */ - psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk, + vlan_id); /* * This is about to iterate over all psks, prev_psk gives the last * returned psk which should not be returned again. @@ -291,6 +299,9 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, */ if (sta && sta->psk && !psk) { struct hostapd_sta_wpa_psk_short *pos; + + if (vlan_id) + *vlan_id = 0; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { if (pos->is_passphrase) { @@ -776,6 +787,74 @@ static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, } +static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci) +{ + struct hostapd_data *hapd = ctx; + + return hostapd_drv_channel_info(hapd, ci); +} + + +static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id) +{ +#ifndef CONFIG_NO_VLAN + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct vlan_description vlan_desc; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + os_memset(&vlan_desc, 0, sizeof(vlan_desc)); + vlan_desc.notempty = 1; + vlan_desc.untagged = vlan_id; + if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) { + wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file", + vlan_id); + return -1; + } + + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) { + wpa_printf(MSG_INFO, + "Failed to assign VLAN ID %d from wpa_psk_file to " + MACSTR, vlan_id, MAC2STR(sta->addr)); + return -1; + } + + wpa_printf(MSG_INFO, + "Assigned VLAN ID %d from wpa_psk_file to " MACSTR, + vlan_id, MAC2STR(sta->addr)); + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) + return -1; +#endif /* CONFIG_NO_VLAN */ + + return 0; +} + + +#ifdef CONFIG_OCV +static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr, + int ap_max_chanwidth, int ap_seg1_idx, + int *bandwidth, int *seg1_idx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO, + "Failed to get STA info to validate received OCI"); + return -1; + } + + return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth, + seg1_idx); +} +#endif /* CONFIG_OCV */ + + #ifdef CONFIG_IEEE80211R_AP static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, @@ -814,12 +893,18 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) struct hostapd_data *hapd = ctx; struct sta_info *sta; + wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR + " based on WPA authenticator callback", + MAC2STR(sta_addr)); if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) return NULL; sta = ap_sta_add(hapd, sta_addr); if (sta == NULL) return NULL; + if (hapd->driver && hapd->driver->add_sta_node) + sta->added_unassoc = 1; + sta->ft_over_ds = 1; if (sta->wpa_sm) { sta->auth_alg = WLAN_AUTH_FT; return sta->wpa_sm; @@ -1189,6 +1274,11 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .for_each_auth = hostapd_wpa_auth_for_each_auth, .send_ether = hostapd_wpa_auth_send_ether, .send_oui = hostapd_wpa_auth_send_oui, + .channel_info = hostapd_channel_info, + .update_vlan = hostapd_wpa_auth_update_vlan, +#ifdef CONFIG_OCV + .get_sta_tx_params = hostapd_get_sta_tx_params, +#endif /* CONFIG_OCV */ #ifdef CONFIG_IEEE80211R_AP .send_ft_action = hostapd_wpa_auth_send_ft_action, .add_sta = hostapd_wpa_auth_add_sta, diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index b1cea1b49b14..4babd0cbb044 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -22,6 +22,7 @@ struct wpa_state_machine { u8 addr[ETH_ALEN]; u8 p2p_dev_addr[ETH_ALEN]; + u16 auth_alg; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -92,6 +93,9 @@ struct wpa_state_machine { #endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; unsigned int pmkid_set:1; +#ifdef CONFIG_OCV + unsigned int ocv_enabled:1; +#endif /* CONFIG_OCV */ u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -115,6 +119,8 @@ struct wpa_state_machine { u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the * first 384 bits of MSK */ size_t xxkey_len; + u8 pmk_r1[PMK_LEN_MAX]; + unsigned int pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ @@ -147,6 +153,10 @@ struct wpa_state_machine { unsigned int fils_completed:1; #endif /* CONFIG_FILS */ +#ifdef CONFIG_DPP2 + struct wpabuf *dpp_z; +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_TESTING_OPTIONS void (*eapol_status_cb)(void *ctx1, void *ctx2); void *eapol_status_cb_ctx1; @@ -282,8 +292,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, const u8 *anonce, const u8 *snonce, 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); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, 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 cdcc5de39d45..8580a5a69be8 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -293,9 +293,13 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) - capab |= BIT(8) | BIT(14) | BIT(15); + capab |= BIT(8) | BIT(15); #endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; @@ -414,6 +418,10 @@ static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid) capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + if (conf->ocv) + capab |= WPA_CAPABILITY_OCVC; +#endif /* CONFIG_OCV */ WPA_PUT_LE16(eid, capab); eid += 2; @@ -522,7 +530,7 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, + struct wpa_state_machine *sm, int freq, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *mdie, size_t mdie_len, const u8 *owe_dh, size_t owe_dh_len) @@ -552,6 +560,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, if (version == WPA_PROTO_RSN) { res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + if (!data.has_pairwise) + data.pairwise_cipher = wpa_default_rsn_cipher(freq); + if (!data.has_group) + data.group_cipher = wpa_default_rsn_cipher(freq); + + if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie && + !wpa_key_mgmt_only_ft(data.key_mgmt)) { + /* Workaround for some HP and Epson printers that seem + * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP + * advertised RSNE to Association Request frame. */ + wpa_printf(MSG_DEBUG, + "RSN: FT set in RSNE AKM but MDE is missing from " + MACSTR + " - ignore FT AKM(s) because there's also a non-FT AKM", + MAC2STR(sm->addr)); + data.key_mgmt &= ~WPA_KEY_MGMT_FT; + } selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (0) { @@ -760,6 +785,17 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OCV + if ((data.capabilities & WPA_CAPABILITY_OCVC) && + !(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, + "Management frame protection required with OCV, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + wpa_auth_set_ocv(sm, wpa_auth->conf.ocv && + (data.capabilities & WPA_CAPABILITY_OCVC)); +#endif /* CONFIG_OCV */ + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; @@ -799,6 +835,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, "OWE: No Diffie-Hellman Parameter element"); return WPA_INVALID_AKMP; } +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) { + /* Diffie-Hellman Parameter element can be used with DPP as + * well, so allow this to proceed. */ + } else +#endif /* CONFIG_DPP */ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) { wpa_printf(MSG_DEBUG, "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM"); @@ -816,6 +858,21 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else sm->wpa = WPA_VERSION_WPA; +#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS) + if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 || + sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) && + (sm->auth_alg == WLAN_AUTH_FILS_SK || + sm->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sm->auth_alg == WLAN_AUTH_FILS_PK) && + (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid || + os_memcmp_const(data.pmkid, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKR1Name match for FILS+FT"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */ + sm->pmksa = NULL; for (i = 0; i < data.num_pmkid; i++) { wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", @@ -995,6 +1052,15 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { + ie->oci = pos + 2 + RSN_SELECTOR_LEN; + ie->oci_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_OCV */ + return 0; } @@ -1062,6 +1128,23 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm) } +#ifdef CONFIG_OCV + +void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv) +{ + if (sm) + sm->ocv_enabled = ocv; +} + + +int wpa_auth_uses_ocv(struct wpa_state_machine *sm) +{ + return sm ? sm->ocv_enabled : 0; +} + +#endif /* CONFIG_OCV */ + + #ifdef CONFIG_OWE u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 *pos, size_t max_len, diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h index 73e433349049..a38b206fd0f4 100644 --- a/src/ap/wpa_auth_ie.h +++ b/src/ap/wpa_auth_ie.h @@ -33,6 +33,10 @@ struct wpa_eapol_ie_parse { const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ const u8 *osen; size_t osen_len; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 5ec019971f37..6161cdbdb922 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -354,6 +354,18 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, bss->wpa_pairwise, bss->rsn_pairwise); + if (hapd->conf->wps_cred_add_sae && + (cred->auth_type & WPS_AUTH_WPA2PSK) && + cred->key_len != 2 * PMK_LEN) { + bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE; +#ifdef CONFIG_IEEE80211W + if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) + bss->ieee80211w = + MGMT_FRAME_PROTECTION_OPTIONAL; + bss->sae_require_mfp = 1; +#endif /* CONFIG_IEEE80211W */ + } + 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); @@ -401,6 +413,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) char buf[1024]; int multi_bss; int wpa; + int pmf_changed = 0; if (hapd->wps == NULL) return 0; @@ -520,6 +533,10 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) if (wpa) { char *prefix; +#ifdef CONFIG_IEEE80211W + int sae = 0; +#endif /* CONFIG_IEEE80211W */ + fprintf(nconf, "wpa=%d\n", wpa); fprintf(nconf, "wpa_key_mgmt="); @@ -528,10 +545,30 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) fprintf(nconf, "WPA-EAP"); prefix = " "; } - if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) { fprintf(nconf, "%sWPA-PSK", prefix); + prefix = " "; + } + if (hapd->conf->wps_cred_add_sae && + (cred->auth_type & WPS_AUTH_WPA2PSK) && + cred->key_len != 2 * PMK_LEN) { + fprintf(nconf, "%sSAE", prefix); +#ifdef CONFIG_IEEE80211W + sae = 1; +#endif /* CONFIG_IEEE80211W */ + } fprintf(nconf, "\n"); +#ifdef CONFIG_IEEE80211W + if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + fprintf(nconf, "ieee80211w=%d\n", + MGMT_FRAME_PROTECTION_OPTIONAL); + pmf_changed = 1; + } + if (sae) + fprintf(nconf, "sae_require_mfp=1\n"); +#endif /* CONFIG_IEEE80211W */ + fprintf(nconf, "wpa_pairwise="); prefix = ""; if (cred->encr_type & WPS_ENCR_AES) { @@ -585,6 +622,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) str_starts(buf, "wep_default_key=") || str_starts(buf, "wep_key") || str_starts(buf, "wps_state=") || + (pmf_changed && str_starts(buf, "ieee80211w=")) || str_starts(buf, "wpa=") || str_starts(buf, "wpa_psk=") || str_starts(buf, "wpa_pairwise=") || @@ -975,6 +1013,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, { struct wps_context *wps; struct wps_registrar_config cfg; + u8 *multi_ap_netw_key = NULL; if (conf->wps_state == 0) { hostapd_wps_clear_ies(hapd, 0); @@ -1133,6 +1172,31 @@ int hostapd_init_wps(struct hostapd_data *hapd, wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; } + if ((hapd->conf->multi_ap & FRONTHAUL_BSS) && + hapd->conf->multi_ap_backhaul_ssid.ssid_len) { + cfg.multi_ap_backhaul_ssid_len = + hapd->conf->multi_ap_backhaul_ssid.ssid_len; + cfg.multi_ap_backhaul_ssid = + hapd->conf->multi_ap_backhaul_ssid.ssid; + + if (conf->multi_ap_backhaul_ssid.wpa_passphrase) { + cfg.multi_ap_backhaul_network_key = (const u8 *) + conf->multi_ap_backhaul_ssid.wpa_passphrase; + cfg.multi_ap_backhaul_network_key_len = + os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase); + } else if (conf->multi_ap_backhaul_ssid.wpa_psk) { + multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1); + if (!multi_ap_netw_key) + goto fail; + wpa_snprintf_hex((char *) multi_ap_netw_key, + 2 * PMK_LEN + 1, + conf->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN); + cfg.multi_ap_backhaul_network_key = multi_ap_netw_key; + cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN; + } + } + wps->ap_settings = conf->ap_settings; wps->ap_settings_len = conf->ap_settings_len; @@ -1174,10 +1238,12 @@ int hostapd_init_wps(struct hostapd_data *hapd, hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); hapd->wps = wps; + bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); return 0; fail: + bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN); hostapd_free_wps(wps); return -1; } diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index 0b596bbcc631..30c52476bbed 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -1,6 +1,6 @@ /* * common module tests - * Copyright (c) 2014-2015, Jouni Malinen + * Copyright (c) 2014-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,10 +10,12 @@ #include "utils/common.h" #include "utils/module_tests.h" +#include "crypto/crypto.h" #include "ieee802_11_common.h" #include "ieee802_11_defs.h" #include "gas.h" #include "wpa_common.h" +#include "sae.h" struct ieee802_11_parse_test_data { @@ -248,6 +250,179 @@ static int gas_tests(void) } +static int sae_tests(void) +{ +#ifdef CONFIG_SAE + struct sae_data sae; + int ret = -1; + /* IEEE P802.11-REVmd/D2.1, Annex J.10 */ + const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 }; + const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 }; + const char *pw = "mekmitasdigoat"; + const char *pwid = "psk4internet"; + const u8 local_rand[] = { + 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e, + 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd, + 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5, + 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf + }; + const u8 local_mask[] = { + 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c, + 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4, + 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e, + 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15 + }; + const u8 local_commit[] = { + 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4, + 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39, + 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0, + 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4, + 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58, + 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25, + 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5, + 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61, + 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4, + 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98, + 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88, + 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52, + 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, + 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74 + }; + const u8 peer_commit[] = { + 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea, + 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70, + 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55, + 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2, + 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9, + 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d, + 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38, + 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f, + 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc, + 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf, + 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf, + 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62, + 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b, + 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74 + }; + const u8 kck[] = { + 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8, + 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94, + 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3, + 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed + }; + const u8 pmk[] = { + 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21, + 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85, + 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16, + 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5 + }; + const u8 pmkid[] = { + 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00, + 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f + }; + const u8 local_confirm[] = { + 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50, + 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a, + 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca, + 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9, + 0xfc, 0x77 + }; + const u8 peer_confirm[] = { + 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89, + 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe, + 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e, + 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa, + 0xc2, 0xfd + }; + struct wpabuf *buf = NULL; + struct crypto_bignum *mask = NULL; + + os_memset(&sae, 0, sizeof(sae)); + buf = wpabuf_alloc(1000); + if (!buf || + sae_set_group(&sae, 19) < 0 || + sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw), + pwid, &sae) < 0) + goto fail; + + /* Override local values based on SAE test vector */ + crypto_bignum_deinit(sae.tmp->sae_rand, 1); + sae.tmp->sae_rand = crypto_bignum_init_set(local_rand, + sizeof(local_rand)); + mask = crypto_bignum_init_set(local_mask, sizeof(local_mask)); + if (!sae.tmp->sae_rand || !mask) + goto fail; + + if (crypto_bignum_add(sae.tmp->sae_rand, mask, + sae.tmp->own_commit_scalar) < 0 || + crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order, + sae.tmp->own_commit_scalar) < 0 || + 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) + goto fail; + + /* Check that output matches the test vector */ + sae_write_commit(&sae, buf, NULL, pwid); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf); + + if (wpabuf_len(buf) != sizeof(local_commit) || + os_memcmp(wpabuf_head(buf), local_commit, + sizeof(local_commit)) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit"); + goto fail; + } + + if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL, + NULL) != 0 || + sae_process_commit(&sae) < 0) + goto fail; + + if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK"); + goto fail; + } + + if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK"); + goto fail; + } + + if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID"); + goto fail; + } + + buf->used = 0; + sae.send_confirm = 1; + sae_write_confirm(&sae, buf); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf); + + if (wpabuf_len(buf) != sizeof(local_confirm) || + os_memcmp(wpabuf_head(buf), local_confirm, + sizeof(local_confirm)) != 0) { + wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm"); + goto fail; + } + + if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0) + goto fail; + + ret = 0; +fail: + sae_clear_data(&sae); + wpabuf_free(buf); + crypto_bignum_deinit(mask, 1); + return ret; +#else /* CONFIG_SAE */ + return 0; +#endif /* CONFIG_SAE */ +} + + int common_module_tests(void) { int ret = 0; @@ -256,6 +431,7 @@ int common_module_tests(void) if (ieee802_11_parse_tests() < 0 || gas_tests() < 0 || + sae_tests() < 0 || rsn_ie_parse_tests() < 0) ret = -1; diff --git a/src/common/defs.h b/src/common/defs.h index c968cd6cb82a..4faf1c8601d0 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -59,6 +59,13 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_DPP BIT(23) #define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) +#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \ + WPA_KEY_MGMT_FT_IEEE8021X | \ + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \ + WPA_KEY_MGMT_FT_SAE | \ + WPA_KEY_MGMT_FT_FILS_SHA256 | \ + WPA_KEY_MGMT_FT_FILS_SHA384) + static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | @@ -86,12 +93,14 @@ static inline int wpa_key_mgmt_wpa_psk(int akm) static inline int wpa_key_mgmt_ft(int akm) { - return !!(akm & (WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | - WPA_KEY_MGMT_FT_SAE | - WPA_KEY_MGMT_FT_FILS_SHA256 | - WPA_KEY_MGMT_FT_FILS_SHA384)); + return !!(akm & WPA_KEY_MGMT_FT); +} + +static inline int wpa_key_mgmt_only_ft(int akm) +{ + int ft = wpa_key_mgmt_ft(akm); + akm &= ~WPA_KEY_MGMT_FT; + return ft && !akm; } static inline int wpa_key_mgmt_ft_psk(int akm) @@ -399,4 +408,15 @@ enum eap_proxy_sim_state { #define OCE_STA_CFON BIT(1) #define OCE_AP BIT(2) +/* 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 +}; + #endif /* DEFS_H */ diff --git a/src/common/dpp.c b/src/common/dpp.c index e715e0454d72..49de47697384 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -1,6 +1,7 @@ /* * DPP functionality shared between hostapd and wpa_supplicant * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,6 +19,7 @@ #include "common/ieee802_11_common.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/gas.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/aes.h" @@ -68,6 +70,11 @@ static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, #endif +struct dpp_global { + struct dl_list bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list configurator; /* struct dpp_configurator */ +}; + static const struct dpp_curve_params dpp_curves[] = { /* The mandatory to support and the default NIST P-256 curve needs to * be the first entry on this list. */ @@ -813,7 +820,9 @@ static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) const unsigned char *pk; int ppklen; X509_ALGOR *pa; -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20800000L) ASN1_OBJECT *pa_oid; #else const ASN1_OBJECT *pa_oid; @@ -1535,6 +1544,9 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, 4 + sizeof(wrapped_data); if (neg_freq > 0) attr_len += 4 + 2; +#ifdef CONFIG_DPP2 + attr_len += 5; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) attr_len += 5; @@ -1577,6 +1589,13 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, wpabuf_put_u8(msg, channel); } +#ifdef CONFIG_DPP2 + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, 2); +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) { wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); @@ -1703,6 +1722,9 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, /* Build DPP Authentication Response frame attributes */ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data); +#ifdef CONFIG_DPP2 + attr_len += 5; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) attr_len += 5; @@ -1730,6 +1752,13 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, wpabuf_put_buf(msg, pr); } +#ifdef CONFIG_DPP2 + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, 2); +#endif /* CONFIG_DPP2 */ + attr_end = wpabuf_put(msg, 0); #ifdef CONFIG_TESTING_OPTIONS @@ -2200,8 +2229,8 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx, } -struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, - const char *json) +static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, + const char *json) { size_t nonce_len; size_t json_len, clear_len; @@ -2305,6 +2334,55 @@ struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, } +static void dpp_write_adv_proto(struct wpabuf *buf) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); +} + + +static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) +{ + /* GAS Query */ + wpabuf_put_le16(buf, wpabuf_len(query)); + wpabuf_put_buf(buf, query); +} + + +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json) +{ + struct wpabuf *buf, *conf_req; + + conf_req = dpp_build_conf_req_attr(auth, json); + if (!conf_req) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return NULL; + } + + buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); + if (!buf) { + wpabuf_free(conf_req); + return NULL; + } + + dpp_write_adv_proto(buf); + dpp_write_gas_query(buf, conf_req); + wpabuf_free(conf_req); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); + + return buf; +} + + static void dpp_auth_success(struct dpp_authentication *auth) { wpa_printf(MSG_DEBUG, @@ -2891,6 +2969,10 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len, channel_len; struct dpp_authentication *auth = NULL; +#ifdef CONFIG_DPP2 + const u8 *version; + u16 version_len; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { @@ -2920,6 +3002,22 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, auth->curve = own_bi->curve; auth->curr_freq = freq; + auth->peer_version = 1; /* default to the first version */ +#ifdef CONFIG_DPP2 + version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (version) { + if (version_len < 1 || version[0] == 0) { + dpp_auth_fail(auth, + "Invalid Protocol Version attribute"); + goto fail; + } + auth->peer_version = version[0]; + wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", + auth->peer_version); + } +#endif /* CONFIG_DPP2 */ + channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, &channel_len); if (channel) { @@ -3448,6 +3546,10 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, wrapped2_len, r_auth_len; u8 r_auth2[DPP_MAX_HASH_LEN]; u8 role; +#ifdef CONFIG_DPP2 + const u8 *version; + u16 version_len; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { @@ -3522,6 +3624,22 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, return NULL; } + auth->peer_version = 1; /* default to the first version */ +#ifdef CONFIG_DPP2 + version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (version) { + if (version_len < 1 || version[0] == 0) { + dpp_auth_fail(auth, + "Invalid Protocol Version attribute"); + return NULL; + } + auth->peer_version = version[0]; + wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", + auth->peer_version); + } +#endif /* CONFIG_DPP2 */ + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, &status_len); if (!status || status_len < 1) { @@ -3985,6 +4103,99 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, } +static int bin_str_eq(const char *val, size_t len, const char *cmp) +{ + return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; +} + + +struct dpp_configuration * dpp_configuration_alloc(const char *type) +{ + struct dpp_configuration *conf; + const char *end; + size_t len; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + goto fail; + + end = os_strchr(type, ' '); + if (end) + len = end - type; + else + len = os_strlen(type); + + if (bin_str_eq(type, len, "psk")) + conf->akm = DPP_AKM_PSK; + else if (bin_str_eq(type, len, "sae")) + conf->akm = DPP_AKM_SAE; + else if (bin_str_eq(type, len, "psk-sae") || + bin_str_eq(type, len, "psk+sae")) + conf->akm = DPP_AKM_PSK_SAE; + else if (bin_str_eq(type, len, "sae-dpp") || + bin_str_eq(type, len, "dpp+sae")) + conf->akm = DPP_AKM_SAE_DPP; + else if (bin_str_eq(type, len, "psk-sae-dpp") || + bin_str_eq(type, len, "dpp+psk+sae")) + conf->akm = DPP_AKM_PSK_SAE_DPP; + else if (bin_str_eq(type, len, "dpp")) + conf->akm = DPP_AKM_DPP; + else + goto fail; + + return conf; +fail: + dpp_configuration_free(conf); + return NULL; +} + + +int dpp_akm_psk(enum dpp_akm akm) +{ + return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_sae(enum dpp_akm akm) +{ + return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_legacy(enum dpp_akm akm) +{ + return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || + akm == DPP_AKM_SAE; +} + + +int dpp_akm_dpp(enum dpp_akm akm) +{ + return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || + akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_akm_ver2(enum dpp_akm akm) +{ + return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; +} + + +int dpp_configuration_valid(const struct dpp_configuration *conf) +{ + if (conf->ssid_len == 0) + return 0; + if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) + return 0; + if (dpp_akm_sae(conf->akm) && !conf->passphrase) + return 0; + return 1; +} + + void dpp_configuration_free(struct dpp_configuration *conf) { if (!conf) @@ -3995,6 +4206,162 @@ void dpp_configuration_free(struct dpp_configuration *conf) } +static int dpp_configuration_parse(struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos, *end; + struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; + struct dpp_configuration *conf = NULL; + + pos = os_strstr(cmd, " conf=sta-"); + if (pos) { + conf_sta = dpp_configuration_alloc(pos + 10); + if (!conf_sta) + goto fail; + conf = conf_sta; + } + + pos = os_strstr(cmd, " conf=ap-"); + if (pos) { + conf_ap = dpp_configuration_alloc(pos + 9); + if (!conf_ap) + goto fail; + conf = conf_ap; + } + + if (!conf) + return 0; + + pos = os_strstr(cmd, " ssid="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->ssid_len /= 2; + if (conf->ssid_len > sizeof(conf->ssid) || + hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) + goto fail; + } else { +#ifdef CONFIG_TESTING_OPTIONS + /* use a default SSID for legacy testing reasons */ + os_memcpy(conf->ssid, "test", 4); + conf->ssid_len = 4; +#else /* CONFIG_TESTING_OPTIONS */ + goto fail; +#endif /* CONFIG_TESTING_OPTIONS */ + } + + pos = os_strstr(cmd, " pass="); + if (pos) { + size_t pass_len; + + pos += 6; + end = os_strchr(pos, ' '); + pass_len = end ? (size_t) (end - pos) : os_strlen(pos); + pass_len /= 2; + if (pass_len > 63 || pass_len < 8) + goto fail; + conf->passphrase = os_zalloc(pass_len + 1); + if (!conf->passphrase || + hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " psk="); + if (pos) { + pos += 5; + if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) + goto fail; + conf->psk_set = 1; + } + + pos = os_strstr(cmd, " group_id="); + if (pos) { + size_t group_id_len; + + pos += 10; + end = os_strchr(pos, ' '); + group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); + conf->group_id = os_malloc(group_id_len + 1); + if (!conf->group_id) + goto fail; + os_memcpy(conf->group_id, pos, group_id_len); + conf->group_id[group_id_len] = '\0'; + } + + pos = os_strstr(cmd, " expiry="); + if (pos) { + long int val; + + pos += 8; + val = strtol(pos, NULL, 0); + if (val <= 0) + goto fail; + conf->netaccesskey_expiry = val; + } + + if (!dpp_configuration_valid(conf)) + goto fail; + + auth->conf_sta = conf_sta; + auth->conf_ap = conf_ap; + return 0; + +fail: + dpp_configuration_free(conf_sta); + dpp_configuration_free(conf_ap); + return -1; +} + + +static struct dpp_configurator * +dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_configurator *conf; + + if (!dpp) + return NULL; + + dl_list_for_each(conf, &dpp->configurator, + struct dpp_configurator, list) { + if (conf->id == id) + return conf; + } + return NULL; +} + + +int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, + struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos; + + if (!cmd) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); + + pos = os_strstr(cmd, " configurator="); + if (pos) { + pos += 14; + auth->conf = dpp_configurator_get_id(dpp, atoi(pos)); + if (!auth->conf) { + wpa_printf(MSG_INFO, + "DPP: Could not find the specified configurator"); + return -1; + } + } + + if (dpp_configuration_parse(auth, cmd) < 0) { + wpa_msg(msg_ctx, MSG_INFO, + "DPP: Failed to set configurator parameters"); + return -1; + } + return 0; +} + + void dpp_auth_deinit(struct dpp_authentication *auth) { if (!auth) @@ -4094,6 +4461,31 @@ static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key, } +static void dpp_build_legacy_cred_params(struct wpabuf *buf, + struct dpp_configuration *conf) +{ + if (conf->passphrase && os_strlen(conf->passphrase) < 64) { + char pass[63 * 6 + 1]; + + json_escape_string(pass, sizeof(pass), conf->passphrase, + os_strlen(conf->passphrase)); + wpabuf_put_str(buf, "\"pass\":\""); + wpabuf_put_str(buf, pass); + wpabuf_put_str(buf, "\""); + os_memset(pass, 0, sizeof(pass)); + } else if (conf->psk_set) { + char psk[2 * sizeof(conf->psk) + 1]; + + wpa_snprintf_hex(psk, sizeof(psk), + conf->psk, sizeof(conf->psk)); + wpabuf_put_str(buf, "\"psk_hex\":\""); + wpabuf_put_str(buf, psk); + wpabuf_put_str(buf, "\""); + os_memset(psk, 0, sizeof(psk)); + } +} + + static struct wpabuf * dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, struct dpp_configuration *conf) @@ -4114,6 +4506,8 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, const EVP_MD *sign_md; const BIGNUM *r, *s; size_t extra_len = 1000; + int incl_legacy; + enum dpp_akm akm; if (!auth->conf) { wpa_printf(MSG_INFO, @@ -4132,6 +4526,13 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, goto fail; } + akm = conf->akm; + if (dpp_akm_ver2(akm) && auth->peer_version < 2) { + wpa_printf(MSG_DEBUG, + "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); + akm = DPP_AKM_DPP; + } + #ifdef CONFIG_TESTING_OPTIONS if (auth->groups_override) extra_len += os_strlen(auth->groups_override); @@ -4249,14 +4650,22 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, if (!signed3) goto fail; + incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); tailroom = 1000; tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); tailroom += signed1_len + signed2_len + signed3_len; + if (incl_legacy) + tailroom += 1000; buf = dpp_build_conf_start(auth, conf, tailroom); if (!buf) goto fail; - wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\""); + wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm)); + if (incl_legacy) { + dpp_build_legacy_cred_params(buf, conf); + wpabuf_put_str(buf, ","); + } + wpabuf_put_str(buf, "\"signedConnector\":\""); wpabuf_put_str(buf, signed1); wpabuf_put_u8(buf, '.'); wpabuf_put_str(buf, signed2); @@ -4302,28 +4711,7 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, return NULL; wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); - if (conf->passphrase) { - char pass[63 * 6 + 1]; - - if (os_strlen(conf->passphrase) > 63) { - wpabuf_free(buf); - return NULL; - } - - json_escape_string(pass, sizeof(pass), conf->passphrase, - os_strlen(conf->passphrase)); - wpabuf_put_str(buf, "\"pass\":\""); - wpabuf_put_str(buf, pass); - wpabuf_put_str(buf, "\""); - } else { - char psk[2 * sizeof(conf->psk) + 1]; - - wpa_snprintf_hex(psk, sizeof(psk), - conf->psk, sizeof(conf->psk)); - wpabuf_put_str(buf, "\"psk_hex\":\""); - wpabuf_put_str(buf, psk); - wpabuf_put_str(buf, "\""); - } + dpp_build_legacy_cred_params(buf, conf); wpabuf_put_str(buf, "}}"); wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", @@ -4354,7 +4742,7 @@ dpp_build_conf_obj(struct dpp_authentication *auth, int ap) return NULL; } - if (conf->akm == DPP_AKM_DPP) + if (dpp_akm_dpp(conf->akm)) return dpp_build_conf_obj_dpp(auth, ap, conf); return dpp_build_conf_obj_legacy(auth, ap, conf); } @@ -4378,6 +4766,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, wpabuf_head(conf), wpabuf_len(conf)); } status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; + auth->conf_resp_status = status; /* { E-nonce, configurationObject}ke */ clear_len = 4 + e_nonce_len; @@ -4550,6 +4939,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); config_attr = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_ATTR_OBJ, @@ -4714,7 +5104,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth, os_strlcpy(auth->passphrase, pass->string, sizeof(auth->passphrase)); } else if (psk_hex && psk_hex->type == JSON_STRING) { - if (auth->akm == DPP_AKM_SAE) { + if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) { wpa_printf(MSG_DEBUG, "DPP: Unexpected psk_hex with akm=sae"); return -1; @@ -4732,8 +5122,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth, return -1; } - if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) && - !auth->passphrase[0]) { + if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) { wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); return -1; } @@ -5246,6 +5635,13 @@ static int dpp_parse_cred_dpp(struct dpp_authentication *auth, os_memset(&info, 0, sizeof(info)); + if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) { + wpa_printf(MSG_DEBUG, + "DPP: Legacy credential included in Connector credential"); + if (dpp_parse_cred_legacy(auth, cred) < 0) + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Connector credential"); csign = json_get_member(cred, "csign"); @@ -5311,6 +5707,10 @@ const char * dpp_akm_str(enum dpp_akm akm) return "sae"; case DPP_AKM_PSK_SAE: return "psk+sae"; + case DPP_AKM_SAE_DPP: + return "dpp+sae"; + case DPP_AKM_PSK_SAE_DPP: + return "dpp+psk+sae"; default: return "??"; } @@ -5327,6 +5727,10 @@ static enum dpp_akm dpp_akm_from_str(const char *akm) return DPP_AKM_PSK_SAE; if (os_strcmp(akm, "dpp") == 0) return DPP_AKM_DPP; + if (os_strcmp(akm, "dpp+sae") == 0) + return DPP_AKM_SAE_DPP; + if (os_strcmp(akm, "dpp+psk+sae") == 0) + return DPP_AKM_PSK_SAE_DPP; return DPP_AKM_UNKNOWN; } @@ -5390,11 +5794,10 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth, } auth->akm = dpp_akm_from_str(token->string); - if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE || - auth->akm == DPP_AKM_PSK_SAE) { + if (dpp_akm_legacy(auth->akm)) { if (dpp_parse_cred_legacy(auth, cred) < 0) goto fail; - } else if (auth->akm == DPP_AKM_DPP) { + } else if (dpp_akm_dpp(auth->akm)) { if (dpp_parse_cred_dpp(auth, cred) < 0) goto fail; } else { @@ -5423,6 +5826,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, size_t unwrapped_len = 0; int ret = -1; + auth->conf_resp_status = 255; + if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { dpp_auth_fail(auth, "Invalid attribute in config response"); return -1; @@ -5483,6 +5888,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, "Missing or invalid required DPP Status attribute"); goto fail; } + auth->conf_resp_status = status[0]; wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); if (status[0] != DPP_STATUS_OK) { dpp_auth_fail(auth, "Configurator rejected configuration"); @@ -5509,6 +5915,146 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, } +#ifdef CONFIG_DPP2 +enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + const u8 *wrapped_data, *status, *e_nonce; + u16 wrapped_data_len, status_len, e_nonce_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + enum dpp_status_error ret = 256; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { + dpp_auth_fail(auth, "Enrollee Nonce mismatch"); + wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", + auth->e_nonce, e_nonce_len); + goto fail; + } + + status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + ret = status[0]; + +fail: + bin_clear_free(unwrapped, unwrapped_len); + return ret; +} +#endif /* CONFIG_DPP2 */ + + +struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg, *clear; + size_t nonce_len, clear_len, attr_len; + const u8 *addr[2]; + size_t len[2]; + u8 *wrapped; + + nonce_len = auth->curve->nonce_len; + clear_len = 5 + 4 + nonce_len; + attr_len = 4 + clear_len + AES_BLOCK_SIZE; + clear = wpabuf_alloc(clear_len); + msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len); + if (!clear || !msg) + return NULL; + + /* DPP Status */ + dpp_build_attr_status(clear, status); + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len); + wpabuf_put_data(clear, auth->e_nonce, nonce_len); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data (none) */ + addr[1] = wpabuf_put(msg, 0); + len[1] = 0; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + /* Wrapped Data */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg); + wpabuf_free(clear); + return msg; +fail: + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + + void dpp_configurator_free(struct dpp_configurator *conf) { if (!conf) @@ -7689,3 +8235,487 @@ char * dpp_corrupt_connector_signature(const char *connector) goto out; } #endif /* CONFIG_TESTING_OPTIONS */ + + +#ifdef CONFIG_DPP2 + +struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, + size_t net_access_key_len) +{ + struct wpabuf *pub = NULL; + EVP_PKEY *own_key; + struct dpp_pfs *pfs; + + pfs = os_zalloc(sizeof(*pfs)); + if (!pfs) + return NULL; + + own_key = dpp_set_keypair(&pfs->curve, net_access_key, + net_access_key_len); + if (!own_key) { + wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); + goto fail; + } + EVP_PKEY_free(own_key); + + pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group); + if (!pfs->ecdh) + goto fail; + + pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0); + pub = wpabuf_zeropad(pub, pfs->curve->prime_len); + if (!pub) + goto fail; + + pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub)); + if (!pfs->ie) + goto fail; + wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(pfs->ie, pfs->curve->ike_group); + wpabuf_put_buf(pfs->ie, pub); + wpabuf_free(pub); + wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element", + pfs->ie); + + return pfs; +fail: + wpabuf_free(pub); + dpp_pfs_free(pfs); + return NULL; +} + + +int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len) +{ + if (peer_ie_len < 2) + return -1; + if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) { + wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS"); + return -1; + } + + pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2, + peer_ie_len - 2); + pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len); + if (!pfs->secret) { + wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key"); + return -1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret); + return 0; +} + + +void dpp_pfs_free(struct dpp_pfs *pfs) +{ + if (!pfs) + return; + crypto_ecdh_deinit(pfs->ecdh); + wpabuf_free(pfs->ie); + wpabuf_clear_free(pfs->secret); + os_free(pfs); +} + +#endif /* CONFIG_DPP2 */ + + +static unsigned int dpp_next_id(struct dpp_global *dpp) +{ + struct dpp_bootstrap_info *bi; + unsigned int max_id = 0; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (bi->id > max_id) + max_id = bi->id; + } + return max_id + 1; +} + + +static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi, *tmp; + int found = 0; + + if (!dpp) + return -1; + + dl_list_for_each_safe(bi, tmp, &dpp->bootstrap, + struct dpp_bootstrap_info, list) { + if (id && bi->id != id) + continue; + found = 1; + dl_list_del(&bi->list); + dpp_bootstrap_info_free(bi); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, + const char *uri) +{ + struct dpp_bootstrap_info *bi; + + if (!dpp) + return NULL; + + bi = dpp_parse_qr_code(uri); + if (!bi) + return NULL; + + bi->id = dpp_next_id(dpp); + dl_list_add(&dpp->bootstrap, &bi->list); + return bi; +} + + +int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd) +{ + char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + size_t len; + int ret = -1; + struct dpp_bootstrap_info *bi; + + if (!dpp) + return -1; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + goto fail; + + if (os_strstr(cmd, "type=qrcode")) + bi->type = DPP_BOOTSTRAP_QR_CODE; + else if (os_strstr(cmd, "type=pkex")) + bi->type = DPP_BOOTSTRAP_PKEX; + else + goto fail; + + chan = get_param(cmd, " chan="); + mac = get_param(cmd, " mac="); + info = get_param(cmd, " info="); + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + pk = dpp_keygen(bi, curve, privkey, privkey_len); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + if (chan) { + if (dpp_parse_uri_chan_list(bi, chan) < 0) + goto fail; + len += 3 + os_strlen(chan); /* C:...; */ + } + if (mac) { + if (dpp_parse_uri_mac(bi, mac) < 0) + goto fail; + len += 3 + os_strlen(mac); /* M:...; */ + } + if (info) { + if (dpp_parse_uri_info(bi, info) < 0) + goto fail; + len += 3 + os_strlen(info); /* I:...; */ + } + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", + chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", + mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", + info ? "I:" : "", info ? info : "", info ? ";" : "", + pk); + bi->id = dpp_next_id(dpp); + dl_list_add(&dpp->bootstrap, &bi->list); + ret = bi->id; + bi = NULL; +fail: + os_free(curve); + os_free(pk); + os_free(chan); + os_free(mac); + os_free(info); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_bootstrap_info_free(bi); + return ret; +} + + +struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + if (!dpp) + return NULL; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (bi->id == id) + return bi; + } + return NULL; +} + + +int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_bootstrap_del(dpp, id_val); +} + + +struct dpp_bootstrap_info * +dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, + unsigned int freq) +{ + struct dpp_bootstrap_info *bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + bi->id = dpp_next_id(dpp); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, peer, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = pkex->own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return NULL; + } + dpp_pkex_free(pkex); + dl_list_add(&dpp->bootstrap, &bi->list); + return bi; +} + + +const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(dpp, id); + if (!bi) + return NULL; + return bi->uri; +} + + +int dpp_bootstrap_info(struct dpp_global *dpp, int id, + char *reply, int reply_size) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(dpp, id); + if (!bi) + return -1; + return os_snprintf(reply, reply_size, "type=%s\n" + "mac_addr=" MACSTR "\n" + "info=%s\n" + "num_freq=%u\n" + "curve=%s\n", + dpp_bootstrap_type_txt(bi->type), + MAC2STR(bi->mac_addr), + bi->info ? bi->info : "", + bi->num_freq, + bi->curve->name); +} + + +void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, + const u8 *r_bootstrap, + struct dpp_bootstrap_info **own_bi, + struct dpp_bootstrap_info **peer_bi) +{ + struct dpp_bootstrap_info *bi; + + *own_bi = NULL; + *peer_bi = NULL; + if (!dpp) + return; + + dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { + if (!*own_bi && bi->own && + os_memcmp(bi->pubkey_hash, r_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching own bootstrapping information"); + *own_bi = bi; + } + + if (!*peer_bi && !bi->own && + os_memcmp(bi->pubkey_hash, i_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching peer bootstrapping information"); + *peer_bi = bi; + } + + if (*own_bi && *peer_bi) + break; + } + +} + + +static unsigned int dpp_next_configurator_id(struct dpp_global *dpp) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator, + list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int dpp_configurator_add(struct dpp_global *dpp, const char *cmd) +{ + char *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + int ret = -1; + struct dpp_configurator *conf = NULL; + + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + conf = dpp_keygen_configurator(curve, privkey, privkey_len); + if (!conf) + goto fail; + + conf->id = dpp_next_configurator_id(dpp); + dl_list_add(&dpp->configurator, &conf->list); + ret = conf->id; + conf = NULL; +fail: + os_free(curve); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_configurator_free(conf); + return ret; +} + + +static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + if (!dpp) + return -1; + + dl_list_for_each_safe(conf, tmp, &dpp->configurator, + struct dpp_configurator, list) { + if (id && conf->id != id) + continue; + found = 1; + dl_list_del(&conf->list); + dpp_configurator_free(conf); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int dpp_configurator_remove(struct dpp_global *dpp, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_configurator_del(dpp, id_val); +} + + +int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, + char *buf, size_t buflen) +{ + struct dpp_configurator *conf; + + conf = dpp_configurator_get_id(dpp, id); + if (!conf) + return -1; + + return dpp_configurator_get_key(conf, buf, buflen); +} + + +struct dpp_global * dpp_global_init(void) +{ + struct dpp_global *dpp; + + dpp = os_zalloc(sizeof(*dpp)); + if (!dpp) + return NULL; + + dl_list_init(&dpp->bootstrap); + dl_list_init(&dpp->configurator); + + return dpp; +} + + +void dpp_global_clear(struct dpp_global *dpp) +{ + if (!dpp) + return; + + dpp_bootstrap_del(dpp, 0); + dpp_configurator_del(dpp, 0); +} + + +void dpp_global_deinit(struct dpp_global *dpp) +{ + dpp_global_clear(dpp); + os_free(dpp); +} diff --git a/src/common/dpp.h b/src/common/dpp.h index 25759088a57e..5a6d8cc79c2c 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -1,6 +1,7 @@ /* * DPP functionality shared between hostapd and wpa_supplicant * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,12 +10,16 @@ #ifndef DPP_H #define DPP_H +#ifdef CONFIG_DPP #include #include "utils/list.h" #include "common/wpa_common.h" #include "crypto/sha256.h" +struct crypto_ecdh; +struct dpp_global; + #define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */ enum dpp_public_action_frame_type { @@ -27,6 +32,7 @@ enum dpp_public_action_frame_type { DPP_PA_PKEX_EXCHANGE_RESP = 8, DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9, DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10, + DPP_PA_CONFIGURATION_RESULT = 11, }; enum dpp_attribute_id { @@ -54,6 +60,8 @@ enum dpp_attribute_id { DPP_ATTR_TRANSACTION_ID = 0x1016, DPP_ATTR_BOOTSTRAP_INFO = 0x1017, DPP_ATTR_CHANNEL = 0x1018, + DPP_ATTR_PROTOCOL_VERSION = 0x1019, + DPP_ATTR_ENVELOPED_DATA = 0x101A, }; enum dpp_status_error { @@ -66,6 +74,7 @@ enum dpp_status_error { DPP_STATUS_RESPONSE_PENDING = 6, DPP_STATUS_INVALID_CONNECTOR = 7, DPP_STATUS_NO_MATCH = 8, + DPP_STATUS_CONFIG_REJECTED = 9, }; #define DPP_CAPAB_ENROLLEE BIT(0) @@ -141,7 +150,9 @@ enum dpp_akm { DPP_AKM_DPP, DPP_AKM_PSK, DPP_AKM_SAE, - DPP_AKM_PSK_SAE + DPP_AKM_PSK_SAE, + DPP_AKM_SAE_DPP, + DPP_AKM_PSK_SAE_DPP, }; struct dpp_configuration { @@ -158,10 +169,12 @@ struct dpp_configuration { /* For legacy configuration */ char *passphrase; u8 psk[32]; + int psk_set; }; struct dpp_authentication { void *msg_ctx; + u8 peer_version; const struct dpp_curve_params *curve; struct dpp_bootstrap_info *peer_bi; struct dpp_bootstrap_info *own_bi; @@ -169,6 +182,7 @@ struct dpp_authentication { u8 waiting_pubkey_hash[SHA256_MAC_LEN]; int response_pending; enum dpp_status_error auth_resp_status; + enum dpp_status_error conf_resp_status; u8 peer_mac_addr[ETH_ALEN]; u8 i_nonce[DPP_MAX_NONCE_LEN]; u8 r_nonce[DPP_MAX_NONCE_LEN]; @@ -204,6 +218,8 @@ struct dpp_authentication { u8 allowed_roles; int configurator; int remove_on_tx_status; + int connect_on_tx_status; + int waiting_conf_result; int auth_success; struct wpabuf *conf_req; const struct wpabuf *conf_resp; /* owned by GAS server */ @@ -336,6 +352,7 @@ enum dpp_test_behavior { DPP_TEST_STOP_AT_AUTH_RESP = 88, DPP_TEST_STOP_AT_AUTH_CONF = 89, DPP_TEST_STOP_AT_CONF_REQ = 90, + DPP_TEST_REJECT_CONFIG = 91, }; extern enum dpp_test_behavior dpp_test; @@ -382,13 +399,28 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len); int dpp_notify_new_qr_code(struct dpp_authentication *auth, struct dpp_bootstrap_info *peer_bi); +struct dpp_configuration * dpp_configuration_alloc(const char *type); +int dpp_akm_psk(enum dpp_akm akm); +int dpp_akm_sae(enum dpp_akm akm); +int dpp_akm_legacy(enum dpp_akm akm); +int dpp_akm_dpp(enum dpp_akm akm); +int dpp_akm_ver2(enum dpp_akm akm); +int dpp_configuration_valid(const struct dpp_configuration *conf); void dpp_configuration_free(struct dpp_configuration *conf); +int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, + struct dpp_authentication *auth, + const char *cmd); void dpp_auth_deinit(struct dpp_authentication *auth); struct wpabuf * dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, size_t attr_len); int dpp_conf_resp_rx(struct dpp_authentication *auth, const struct wpabuf *resp); +enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len); +struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, + enum dpp_status_error status); struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, size_t len); const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); @@ -432,4 +464,42 @@ void dpp_pkex_free(struct dpp_pkex *pkex); char * dpp_corrupt_connector_signature(const char *connector); + +struct dpp_pfs { + struct crypto_ecdh *ecdh; + const struct dpp_curve_params *curve; + struct wpabuf *ie; + struct wpabuf *secret; +}; + +struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, + size_t net_access_key_len); +int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len); +void dpp_pfs_free(struct dpp_pfs *pfs); + +struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, + const char *uri); +int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd); +struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id); +int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id); +struct dpp_bootstrap_info * +dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, + unsigned int freq); +const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id); +int dpp_bootstrap_info(struct dpp_global *dpp, int id, + char *reply, int reply_size); +void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, + const u8 *r_bootstrap, + struct dpp_bootstrap_info **own_bi, + struct dpp_bootstrap_info **peer_bi); +int dpp_configurator_add(struct dpp_global *dpp, const char *cmd); +int dpp_configurator_remove(struct dpp_global *dpp, const char *id); +int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, + char *buf, size_t buflen); +struct dpp_global * dpp_global_init(void); +void dpp_global_clear(struct dpp_global *dpp); +void dpp_global_deinit(struct dpp_global *dpp); + +#endif /* CONFIG_DPP */ #endif /* DPP_H */ diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index db4037927185..49ed80657521 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -87,13 +87,29 @@ 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) { - int ok, j, first; + int ok, first; int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 184, 192 }; size_t k; + struct hostapd_channel_data *p_chan, *s_chan; + const int ht40_plus = pri_chan < sec_chan; - if (pri_chan == sec_chan || !sec_chan) - return 1; /* HT40 not used */ + p_chan = hw_get_channel_chan(mode, pri_chan, NULL); + if (!p_chan) + return 0; + + if (pri_chan == sec_chan || !sec_chan) { + if (chan_pri_allowed(p_chan)) + return 1; /* HT40 not used */ + + wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary", + pri_chan); + return 0; + } + + s_chan = hw_get_channel_chan(mode, sec_chan, NULL); + if (!s_chan) + return 0; wpa_printf(MSG_DEBUG, "HT40: control channel: %d secondary channel: %d", @@ -101,16 +117,9 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_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) { + if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) || + (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) || + (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) { wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", sec_chan); return 0; @@ -553,3 +562,59 @@ int ieee80211ac_cap_check(u32 hw, u32 conf) } #endif /* CONFIG_IEEE80211AC */ + + +u32 num_chan_to_bw(int num_chans) +{ + switch (num_chans) { + case 2: + case 4: + case 8: + return num_chans * 20; + default: + return 20; + } +} + + +/* check if BW is applicable for channel */ +int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, + int ht40_plus, int pri) +{ + u32 bw_mask; + + switch (bw) { + case 20: + bw_mask = HOSTAPD_CHAN_WIDTH_20; + break; + case 40: + /* HT 40 MHz support declared only for primary channel, + * just skip 40 MHz secondary checking */ + if (pri && ht40_plus) + bw_mask = HOSTAPD_CHAN_WIDTH_40P; + else if (pri && !ht40_plus) + bw_mask = HOSTAPD_CHAN_WIDTH_40M; + else + bw_mask = 0; + break; + case 80: + bw_mask = HOSTAPD_CHAN_WIDTH_80; + break; + case 160: + bw_mask = HOSTAPD_CHAN_WIDTH_160; + break; + default: + bw_mask = 0; + break; + } + + return (chan->allowed_bw & bw_mask) == bw_mask; +} + + +/* check if channel is allowed to be used as primary */ +int chan_pri_allowed(const struct hostapd_channel_data *chan) +{ + return !(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20); +} diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index 9cddbd50e56a..eb1f1c57f10f 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -39,4 +39,9 @@ void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, int disabled); int ieee80211ac_cap_check(u32 hw, u32 conf); +u32 num_chan_to_bw(int num_chans); +int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, + int ht40_plus, int pri); +int chan_pri_allowed(const struct hostapd_channel_data *chan); + #endif /* HW_FEATURES_COMMON_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index e1ef27795b99..e42a327449eb 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-2015, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -126,6 +126,10 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->roaming_cons_sel = pos; elems->roaming_cons_sel_len = elen; break; + case MULTI_AP_OUI_TYPE: + elems->multi_ap = pos; + elems->multi_ap_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -266,6 +270,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->password_id = pos; elems->password_id_len = elen; break; + case WLAN_EID_EXT_HE_CAPABILITIES: + elems->he_capabilities = pos; + elems->he_capabilities_len = elen; + break; + case WLAN_EID_EXT_OCV_OCI: + elems->oci = pos; + elems->oci_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, @@ -291,29 +303,17 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { - size_t left = len; - const u8 *pos = start; + const struct element *elem; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); - while (left >= 2) { - u8 id, elen; + if (!start) + return ParseOK; - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - if (show_errors) { - wpa_printf(MSG_DEBUG, "IEEE 802.11 element " - "parse failed (id=%d elen=%d " - "left=%lu)", - id, elen, (unsigned long) left); - wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); - } - return ParseFailed; - } + for_each_element(elem, start, len) { + u8 id = elem->id, elen = elem->datalen; + const u8 *pos = elem->data; switch (id) { case WLAN_EID_SSID: @@ -461,8 +461,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->mic = pos; elems->mic_len = elen; /* after mic everything is encrypted, so stop. */ - left = elen; - break; + goto done; case WLAN_EID_MULTI_BAND: if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, @@ -521,35 +520,33 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, id, elen); break; } - - left -= elen; - pos += elen; } - if (left) + if (!for_each_element_completed(elem, start, len)) { + if (show_errors) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11 element parse failed @%d", + (int) (start + len - (const u8 *) elem)); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); + } return ParseFailed; + } +done: return unknown ? ParseUnknown : ParseOK; } int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { + const struct element *elem; int count = 0; - const u8 *pos, *end; if (ies == NULL) return 0; - pos = ies; - end = ies + ies_len; - - while (end - pos >= 2) { - if (2 + pos[1] > end - pos) - break; + for_each_element(elem, ies, ies_len) count++; - pos += 2 + pos[1]; - } return count; } @@ -559,24 +556,17 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type) { struct wpabuf *buf; - const u8 *end, *pos, *ie; + const struct element *elem, *found = NULL; - pos = ies; - end = ies + ies_len; - ie = NULL; - - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - return NULL; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) { - ie = pos; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && + WPA_GET_BE32(elem->data) == oui_type) { + found = elem; break; } - pos += 2 + pos[1]; } - if (ie == NULL) + if (!found) return NULL; /* No specified vendor IE found */ buf = wpabuf_alloc(ies_len); @@ -587,13 +577,9 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, * There may be multiple vendor IEs in the message, so need to * concatenate their data fields. */ - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) - wpabuf_put_data(buf, pos + 6, pos[1] - 4); - pos += 2 + pos[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) + wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4); } return buf; @@ -896,6 +882,41 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, } +int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, + int sec_channel, u8 *op_class, u8 *channel) +{ + int vht = CHAN_WIDTH_UNKNOWN; + + switch (chanwidth) { + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + vht = VHT_CHANWIDTH_USE_HT; + break; + case CHAN_WIDTH_80: + vht = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + vht = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + vht = VHT_CHANWIDTH_160MHZ; + break; + } + + if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class, + channel) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_WARNING, + "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)", + freq, chanwidth, sec_channel); + return -1; + } + + return 0; +} + + static const char *const us_op_class_cc[] = { "US", "CA", NULL }; @@ -1297,27 +1318,27 @@ const char * fc2str(u16 fc) int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, size_t ies_len) { + const struct element *elem; + os_memset(info, 0, sizeof(*info)); - while (ies_buf && ies_len >= 2 && - info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { - size_t len = 2 + ies_buf[1]; + if (!ies_buf) + return 0; - if (len > ies_len) { - wpa_hexdump(MSG_DEBUG, "Truncated IEs", - ies_buf, ies_len); - return -1; - } + for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) { + if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED) + return 0; - if (ies_buf[0] == WLAN_EID_MULTI_BAND) { - wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); - info->ies[info->nof_ies].ie = ies_buf + 2; - info->ies[info->nof_ies].ie_len = ies_buf[1]; - info->nof_ies++; - } + wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", + elem->datalen + 2); + info->ies[info->nof_ies].ie = elem->data; + info->ies[info->nof_ies].ie_len = elem->datalen; + info->nof_ies++; + } - ies_len -= len; - ies_buf += len; + if (!for_each_element_completed(elem, ies_buf, ies_len)) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len); + return -1; } return 0; @@ -1440,22 +1461,13 @@ size_t global_op_class_size = ARRAY_SIZE(global_op_class); */ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; - - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - - if (ies[0] == eid) - return ies; - - ies += 2 + ies[1]; - } + for_each_element_id(elem, eid, ies, len) + return &elem->id; return NULL; } @@ -1473,22 +1485,26 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) */ const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; + for_each_element_extid(elem, ext, ies, len) + return &elem->id; - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; + return NULL; +} - if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 && - ies[2] == ext) - return ies; - ies += 2 + ies[1]; +const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type) +{ + const struct element *elem; + + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) { + if (elem->datalen >= 4 && + vendor_type == WPA_GET_BE32(elem->data)) + return &elem->id; } return NULL; @@ -1519,6 +1535,26 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) } +size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value) +{ + u8 *pos = buf; + + if (len < 9) + return 0; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = MULTI_AP_OUI_TYPE; + *pos++ = MULTI_AP_SUB_ELEM_TYPE; + *pos++ = 1; /* len */ + *pos++ = value; + + return pos - buf; +} + + static const struct country_op_class us_op_class[] = { { 1, 115 }, { 2, 118 }, @@ -1664,6 +1700,27 @@ const struct oper_class_map * get_oper_class(const char *country, u8 op_class) } +int oper_class_bw_to_int(const struct oper_class_map *map) +{ + switch (map->bw) { + case BW20: + return 20; + case BW40PLUS: + case BW40MINUS: + return 40; + case BW80: + return 80; + case BW80P80: + case BW160: + return 160; + case BW2160: + return 2160; + default: + return 0; + } +} + + int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len) { @@ -1764,3 +1821,11 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, return nei_pos - nei_rep; } + + +int ieee802_11_ext_capab(const u8 *ie, unsigned int capab) +{ + if (!ie || ie[1] <= capab / 8) + return 0; + return !!(ie[2 + capab / 8] & BIT(capab % 8)); +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index ff7e51de3dc9..d41bd39e7a93 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,13 @@ #define IEEE802_11_COMMON_H #include "defs.h" +#include "ieee802_11_defs.h" + +struct element { + u8 id; + u8 datalen; + u8 data[]; +} STRUCT_PACKED; struct hostapd_hw_modes; @@ -84,6 +91,9 @@ struct ieee802_11_elems { const u8 *power_capab; const u8 *roaming_cons_sel; const u8 *password_id; + const u8 *oci; + const u8 *multi_ap; + const u8 *he_capabilities; u8 ssid_len; u8 supp_rates_len; @@ -130,6 +140,9 @@ struct ieee802_11_elems { u8 power_capab_len; u8 roaming_cons_sel_len; u8 password_id_len; + u8 oci_len; + u8 multi_ap_len; + u8 he_capabilities_len; struct mb_ies_info mb_ies; }; @@ -160,6 +173,8 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel); +int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, + int sec_channel, u8 *op_class, u8 *channel); int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, u16 num_modes); enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); @@ -186,9 +201,12 @@ extern size_t global_op_class_size; const u8 * get_ie(const u8 *ies, size_t len, u8 eid); const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext); +const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type); size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); +size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value); + struct country_op_class { u8 country_op_class; u8 global_op_class; @@ -197,8 +215,58 @@ struct country_op_class { u8 country_to_global_op_class(const char *country, u8 op_class); const struct oper_class_map * get_oper_class(const char *country, u8 op_class); +int oper_class_bw_to_int(const struct oper_class_map *map); int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len); +int ieee802_11_ext_capab(const u8 *ie, unsigned int capab); + +/* element iteration helpers */ +#define for_each_element(_elem, _data, _datalen) \ + for (_elem = (const struct element *) (_data); \ + (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \ + (int) sizeof(*_elem) && \ + (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \ + (int) sizeof(*_elem) + _elem->datalen; \ + _elem = (const struct element *) (_elem->data + _elem->datalen)) + +#define for_each_element_id(element, _id, data, datalen) \ + for_each_element(element, data, datalen) \ + if (element->id == (_id)) + +#define for_each_element_extid(element, extid, _data, _datalen) \ + for_each_element(element, _data, _datalen) \ + if (element->id == WLAN_EID_EXTENSION && \ + element->datalen > 0 && \ + element->data[0] == (extid)) + +#define for_each_subelement(sub, element) \ + for_each_element(sub, (element)->data, (element)->datalen) + +#define for_each_subelement_id(sub, id, element) \ + for_each_element_id(sub, id, (element)->data, (element)->datalen) + +#define for_each_subelement_extid(sub, extid, element) \ + for_each_element_extid(sub, extid, (element)->data, (element)->datalen) + +/** + * for_each_element_completed - Determine if element parsing consumed all data + * @element: Element pointer after for_each_element() or friends + * @data: Same data pointer as passed to for_each_element() or friends + * @datalen: Same data length as passed to for_each_element() or friends + * + * This function returns 1 if all the data was parsed or considered + * while walking the elements. Only use this if your for_each_element() + * loop cannot be broken out of, otherwise it always returns 0. + * + * If some data was malformed, this returns %false since the last parsed + * element will not fill the whole remaining data. + */ +static inline int for_each_element_completed(const struct element *element, + const void *data, size_t datalen) +{ + return (const u8 *) element == (const u8 *) data + datalen; +} + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 762e731ab022..adaa8931093e 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-2015, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * Copyright (c) 2007-2008 Intel Corporation * * This software may be distributed under the terms of the BSD license. @@ -334,7 +334,7 @@ #define WLAN_EID_LOCATION_PARAMETERS 82 #define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83 #define WLAN_EID_SSID_LIST 84 -#define WLAN_EID_MLTIPLE_BSSID_INDEX 85 +#define WLAN_EID_MULTIPLE_BSSID_INDEX 85 #define WLAN_EID_FMS_DESCRIPTOR 86 #define WLAN_EID_FMS_REQUEST 87 #define WLAN_EID_FMS_RESPONSE 88 @@ -467,7 +467,89 @@ #define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 #define WLAN_EID_EXT_HE_CAPABILITIES 35 #define WLAN_EID_EXT_HE_OPERATION 36 +#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38 +#define WLAN_EID_EXT_OCV_OCI 54 +/* Extended Capabilities field */ +#define WLAN_EXT_CAPAB_20_40_COEX 0 +#define WLAN_EXT_CAPAB_GLK 1 +#define WLAN_EXT_CAPAB_EXT_CHAN_SWITCH 2 +#define WLAN_EXT_CAPAB_GLK_GCR 3 +#define WLAN_EXT_CAPAB_PSMP 4 +/* 5 - Reserved */ +#define WLAN_EXT_CAPAB_S_PSMP 6 +#define WLAN_EXT_CAPAB_EVENT 7 +#define WLAN_EXT_CAPAB_DIAGNOSTICS 8 +#define WLAN_EXT_CAPAB_MULTICAST_DIAGNOSTICS 9 +#define WLAN_EXT_CAPAB_LOCATION_TRACKING 10 +#define WLAN_EXT_CAPAB_FMS 11 +#define WLAN_EXT_CAPAB_PROXY_ARP 12 +#define WLAN_EXT_CAPAB_COLL_INTERF_REP 13 +#define WLAN_EXT_CAPAB_CIVIC_LOCATION 14 +#define WLAN_EXT_CAPAB_GEOSPATIAL_LOCATION 15 +#define WLAN_EXT_CAPAB_TFS 16 +#define WLAN_EXT_CAPAB_WNM_SLEEP_MODE 17 +#define WLAN_EXT_CAPAB_TIM_BROADCAST 18 +#define WLAN_EXT_CAPAB_BSS_TRANSITION 19 +#define WLAN_EXT_CAPAB_QOS_TRAFFIC 20 +#define WLAN_EXT_CAPAB_AC_STA_COUNT 21 +#define WLAN_EXT_CAPAB_MULTIPLE_BSSID 22 +#define WLAN_EXT_CAPAB_TIMING_MEASUREMENT 23 +#define WLAN_EXT_CAPAB_CHANNEL_USAGE 24 +#define WLAN_EXT_CAPAB_SSID_LIST 25 +#define WLAN_EXT_CAPAB_DMS 26 +#define WLAN_EXT_CAPAB_UTF_TSF_OFFSET 27 +#define WLAN_EXT_CAPAB_TPU_BUFFER_STA 28 +#define WLAN_EXT_CAPAB_TDLS_PEER_PSM 29 +#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH 30 +#define WLAN_EXT_CAPAB_INTERWORKING 31 +#define WLAN_EXT_CAPAB_QOS_MAP 32 +#define WLAN_EXT_CAPAB_EBR 33 +#define WLAN_EXT_CAPAB_SSPN_INTERFACE 34 +/* 35 - Reserved */ +#define WLAN_EXT_CAPAB_MSGCF 36 +#define WLAN_EXT_CAPAB_TDLS 37 +#define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38 +#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39 +#define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40 +#define WLAN_EXT_CAPAB_ +/* 41-43 - Service Interval Granularity */ +#define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44 +#define WLAN_EXT_CAPAB_U_APSD_COEX 45 +#define WLAN_EXT_CAPAB_WNM_NOTIFCATION 46 +#define WLAN_EXT_CAPAB_QAB 47 +#define WLAN_EXT_CAPAB_UTF_8_SSID 48 +#define WLAN_EXT_CAPAB_QMF 49 +#define WLAN_EXT_CAPAB_QMF_RECONFIG 50 +#define WLAN_EXT_CAPAB_ROBUST_AV_STREAMING 51 +#define WLAN_EXT_CAPAB_ADVANCED_GCR 52 +#define WLAN_EXT_CAPAB_MESH_GCR 53 +#define WLAN_EXT_CAPAB_SCS 54 +#define WLAN_EXT_CAPAB_QLOAD_REPORT 55 +#define WLAN_EXT_CAPAB_ALT_EDCA 56 +#define WLAN_EXT_CAPAB_UNPROT_TXOP_NEG 57 +#define WLAN_EXT_CAPAB_PROT_TXOP_NEG 58 +/* 59 - Reserved */ +#define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60 +#define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61 +#define WLAN_EXT_CAPAB_OPMODE_NOTIF 62 +#define WLAN_EXT_CAPAB_ +/* 63-64 - Max Number of MSDUs In A-MSDU */ +#define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65 +#define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66 +#define WLAN_EXT_CAPAB_NETWORK_CHANNEL_CTRL 67 +#define WLAN_EXT_CAPAB_WHITE_SPACE_MAP 68 +#define WLAN_EXT_CAPAB_CHANNEL_AVAIL_QUERY 69 +#define WLAN_EXT_CAPAB_FTM_RESPONDER 70 +#define WLAN_EXT_CAPAB_FTM_INITIATOR 71 +#define WLAN_EXT_CAPAB_FILS 72 +#define WLAN_EXT_CAPAB_EXT_SPECTRUM_MGMT 73 +#define WLAN_EXT_CAPAB_FUTURE_CHANNEL_GUIDANCE 74 +#define WLAN_EXT_CAPAB_PAD 75 +/* 76-79 - Reserved */ +#define WLAN_EXT_CAPAB_COMPLETE_NON_TX_BSSID_PROFILE 80 +#define WLAN_EXT_CAPAB_SAE_PW_ID 81 +#define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82 /* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 @@ -865,10 +947,12 @@ struct ieee80211_mgmt { struct { u8 action; u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + u8 variable[]; /* OCI element */ } STRUCT_PACKED sa_query_req; struct { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + u8 variable[]; /* OCI element */ } STRUCT_PACKED sa_query_resp; struct { u8 action; @@ -1210,6 +1294,13 @@ struct ieee80211_ampe_ie { #define MBO_OUI_TYPE 22 #define OWE_IE_VENDOR_TYPE 0x506f9a1c #define OWE_OUI_TYPE 28 +#define MULTI_AP_OUI_TYPE 0x1B + +#define MULTI_AP_SUB_ELEM_TYPE 0x06 +#define MULTI_AP_TEAR_DOWN BIT(4) +#define MULTI_AP_FRONTHAUL_BSS BIT(5) +#define MULTI_AP_BACKHAUL_BSS BIT(6) +#define MULTI_AP_BACKHAUL_STA BIT(7) #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -1347,13 +1438,15 @@ enum wmm_ac { #define HS20_PPS_MO_ID_PRESENT 0x02 #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 #ifndef HS20_VERSION -#define HS20_VERSION 0x10 /* Release 2 */ +#define HS20_VERSION 0x20 /* Release 3 */ #endif /* HS20_VERSION */ /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 -#define HS20_WNM_T_C_ACCEPTANCE 2 +#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2 +#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3 +#define HS20_WNM_T_C_ACCEPTANCE 4 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 @@ -1442,12 +1535,6 @@ enum mbo_transition_reject_reason { MBO_TRANSITION_REJECT_REASON_SERVICES = 6, }; -/* MBO v0.0_r19, 4.4: WNM-Notification vendor subelements */ -enum wfa_wnm_notif_subelem_id { - WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT = 2, - WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3, -}; - /* MBO v0.0_r27, 4.3: MBO ANQP-elements */ #define MBO_ANQP_OUI_TYPE 0x12 #define MBO_ANQP_SUBTYPE_QUERY_LIST 1 @@ -1841,11 +1928,14 @@ enum beacon_report_mode { }; /* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */ +/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for + * Beacon request */ #define WLAN_BEACON_REQUEST_SUBELEM_SSID 0 #define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */ #define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */ #define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10 #define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */ +#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221 /* @@ -1895,9 +1985,21 @@ struct rrm_measurement_beacon_report { } STRUCT_PACKED; /* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */ +/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for + * Beacon report */ #define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1 +#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2 +#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221 +/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the + * Reported Frame Body Fragment ID subelement */ +#define REPORTED_FRAME_BODY_SUBELEM_LEN 4 +#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7) + +/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */ +#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3 + /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ @@ -2000,14 +2102,15 @@ enum nr_chan_width { }; struct ieee80211_he_capabilities { - u8 he_mac_capab_info[5]; - u8 he_phy_capab_info[9]; + u8 he_mac_capab_info[6]; + u8 he_phy_capab_info[11]; u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */ /* PPE Thresholds (optional) */ } STRUCT_PACKED; struct ieee80211_he_operation { - u32 he_oper_params; + u32 he_oper_params; /* HE Operation Parameters[3] and + * BSS Color Information[1] */ u8 he_mcs_nss_set[2]; u8 vht_op_info_chwidth; u8 vht_op_info_chan_center_freq_seg0_idx; @@ -2024,28 +2127,55 @@ struct ieee80211_he_operation { #define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1)) /* HE Operation defines */ +/* HE Operation Parameters and BSS Color Information fields */ #define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \ BIT(2) | BIT(3) | \ BIT(4) | BIT(5))) -#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(6) | BIT(7) | \ - BIT(8))) -#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 6 -#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(9)) -#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(10) | BIT(11) | \ - BIT(12) | BIT(13) | \ +#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(6)) +#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(7)) +#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(8) | BIT(9) | \ + BIT(10))) +#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 8 +#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(11)) +#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(12) | BIT(13) | \ BIT(14) | BIT(15) | \ BIT(16) | BIT(17) | \ - BIT(18) | BIT(19))) -#define HE_OPERATION_RTS_THRESHOLD_OFFSET 10 -#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(20)) -#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK ((u32) (BIT(21) | BIT(22) | \ - BIT(23) | BIT(24) | \ - BIT(25) | BIT(26) | \ - BIT(27) | BIT(28))) -#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21 -#define HE_OPERATION_TX_BSSID_INDICATOR ((u32) BIT(29)) -#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(30)) -#define HE_OPERATION_BSS_DUAL_BEACON ((u32) BIT(31)) + BIT(18) | BIT(19) | \ + BIT(20) | BIT(21))) +#define HE_OPERATION_RTS_THRESHOLD_OFFSET 12 + +struct ieee80211_he_mu_edca_parameter_set { + u8 he_qos_info; + u8 he_mu_ac_be_param[3]; + u8 he_mu_ac_bk_param[3]; + u8 he_mu_ac_vi_param[3]; + u8 he_mu_ac_vo_param[3]; +} STRUCT_PACKED; + +/* HE MU AC parameter record field format */ +/* ACI/AIFSN */ +#define HE_MU_AC_PARAM_ACI_IDX 0 +#define HE_MU_AC_PARAM_AIFSN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3))) +#define HE_MU_AC_PARAM_ACM ((u8) BIT(4)) +#define HE_MU_AC_PARAM_ACI ((u8) (BIT(5) | BIT(6))) +/* B7: Reserved */ + +/* ECWmin/ECWmax */ +#define HE_MU_AC_PARAM_ECW_IDX 1 +#define HE_MU_AC_PARAM_ECWMIN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3))) +#define HE_MU_AC_PARAM_ECWMAX ((u8) (BIT(4) | BIT(5) | BIT(6) | BIT(7))) + +/* MU EDCA Timer */ +#define HE_MU_AC_PARAM_TIMER_IDX 2 + +/* HE QoS Info field */ +#define HE_QOS_INFO_EDCA_PARAM_SET_COUNT ((u8) (BIT(0) | BIT(1) | \ + BIT(2) | BIT(3))) +#define HE_QOS_INFO_Q_ACK ((u8) (BIT(4))) +#define HE_QOS_INFO_QUEUE_REQUEST ((u8) (BIT(5))) +#define HE_QOS_INFO_TXOP_REQUEST ((u8) (BIT(6))) +/* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */ +#define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7))) /* DPP Public Action frame identifiers - OUI_WFA */ #define DPP_OUI_TYPE 0x1A diff --git a/src/common/linux_bridge.h b/src/common/linux_bridge.h index 7b768464fb54..84386e60f750 100644 --- a/src/common/linux_bridge.h +++ b/src/common/linux_bridge.h @@ -9,6 +9,21 @@ #ifndef LINUX_BRIDGE_H #define LINUX_BRIDGE_H +/* This ioctl is defined in linux/sockios.h */ + +#ifndef SIOCBRADDBR +#define SIOCBRADDBR 0x89a0 +#endif +#ifndef SIOCBRDELBR +#define SIOCBRDELBR 0x89a1 +#endif +#ifndef SIOCBRADDIF +#define SIOCBRADDIF 0x89a2 +#endif +#ifndef SIOCBRDELIF +#define SIOCBRDELIF 0x89a3 +#endif + /* This interface is defined in linux/if_bridge.h */ #define BRCTL_GET_VERSION 0 diff --git a/src/common/ocv.c b/src/common/ocv.c new file mode 100644 index 000000000000..06badfbfb454 --- /dev/null +++ b/src/common/ocv.c @@ -0,0 +1,172 @@ +/* + * Operating Channel Validation (OCV) + * Copyright (c) 2018, Mathy Vanhoef + * + * 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 "drivers/driver.h" +#include "common/ieee802_11_common.h" +#include "ocv.h" + +/** + * Caller of OCV functionality may use various debug output functions, so store + * the error here and let the caller use an appropriate debug output function. + */ +char ocv_errorstr[256]; + + +int ocv_derive_all_parameters(struct oci_info *oci) +{ + const struct oper_class_map *op_class_map; + + oci->freq = ieee80211_chan_to_freq(NULL, oci->op_class, oci->channel); + if (oci->freq < 0) { + wpa_printf(MSG_INFO, + "Error interpreting OCI: unrecognized opclass/channel pair (%d/%d)", + oci->op_class, oci->channel); + return -1; + } + + op_class_map = get_oper_class(NULL, oci->op_class); + if (!op_class_map) { + wpa_printf(MSG_INFO, + "Error interpreting OCI: Unrecognized opclass (%d)", + oci->op_class); + return -1; + } + + oci->chanwidth = oper_class_bw_to_int(op_class_map); + oci->sec_channel = 0; + if (op_class_map->bw == BW40PLUS) + oci->sec_channel = 1; + else if (op_class_map->bw == BW40MINUS) + oci->sec_channel = -1; + + return 0; +} + + +int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos) +{ + u8 op_class, channel; + u8 *pos = *argpos; + + if (ieee80211_chaninfo_to_channel(ci->frequency, ci->chanwidth, + ci->sec_channel, + &op_class, &channel) < 0) { + wpa_printf(MSG_WARNING, + "Cannot determine operating class and channel for OCI element"); + return -1; + } + + *pos++ = op_class; + *pos++ = channel; + *pos++ = ci->seg1_idx; + + *argpos = pos; + return 0; +} + + +int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos) +{ + u8 *pos = *argpos; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 3; + RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_OCI); + pos += RSN_SELECTOR_LEN; + + *argpos = pos; + return ocv_insert_oci(ci, argpos); +} + + +int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos) +{ + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + OCV_OCI_LEN; + *pos++ = WLAN_EID_EXT_OCV_OCI; + return ocv_insert_oci(ci, &pos); +} + + +int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len, + struct wpa_channel_info *ci, int tx_chanwidth, + int tx_seg1_idx) +{ + struct oci_info oci; + + if (!oci_ie) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: did not receive mandatory OCI"); + return -1; + } + + if (oci_ie_len != 3) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: received OCI of unexpected length (%d)", + (int) oci_ie_len); + return -1; + } + + os_memset(&oci, 0, sizeof(oci)); + oci.op_class = oci_ie[0]; + oci.channel = oci_ie[1]; + oci.seg1_idx = oci_ie[2]; + if (ocv_derive_all_parameters(&oci) != 0) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: unable to interpret received OCI"); + return -1; + } + + /* Primary frequency used to send frames to STA must match the STA's */ + if ((int) ci->frequency != oci.freq) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)", + ci->frequency, oci.freq); + return -1; + } + + /* We shouldn't transmit with a higher bandwidth than the STA supports + */ + if (tx_chanwidth > oci.chanwidth) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)", + tx_chanwidth, oci.chanwidth); + return -1; + } + + /* + * Secondary channel only needs be checked for 40 MHz in the 2.4 GHz + * band. In the 5 GHz band it's verified through the primary frequency. + * Note that the field ci->sec_channel is only filled in when we use + * 40 MHz. + */ + if (tx_chanwidth == 40 && ci->frequency < 2500 && + ci->sec_channel != oci.sec_channel) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)", + ci->sec_channel, oci.sec_channel); + return -1; + } + + /* + * When using a 160 or 80+80 MHz channel to transmit, verify that we use + * the same segments as the receiver by comparing frequency segment 1. + */ + if ((ci->chanwidth == CHAN_WIDTH_160 || + ci->chanwidth == CHAN_WIDTH_80P80) && + tx_seg1_idx != oci.seg1_idx) { + os_snprintf(ocv_errorstr, sizeof(ocv_errorstr), + "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)", + tx_seg1_idx, oci.seg1_idx); + return -1; + } + + return 0; +} diff --git a/src/common/ocv.h b/src/common/ocv.h new file mode 100644 index 000000000000..6379d9d06c9a --- /dev/null +++ b/src/common/ocv.h @@ -0,0 +1,40 @@ +/* + * Operating Channel Validation (OCV) + * Copyright (c) 2018, Mathy Vanhoef + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef OCV_H +#define OCV_H + +struct wpa_channel_info; + +struct oci_info { + /* Values in the OCI element */ + u8 op_class; + u8 channel; + u8 seg1_idx; + + /* Derived values for easier verification */ + int freq; + int sec_channel; + int chanwidth; +}; + +#define OCV_OCI_LEN 3 +#define OCV_OCI_EXTENDED_LEN (3 + OCV_OCI_LEN) +#define OCV_OCI_KDE_LEN (2 + RSN_SELECTOR_LEN + OCV_OCI_LEN) + +extern char ocv_errorstr[256]; + +int ocv_derive_all_parameters(struct oci_info *oci); +int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos); +int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos); +int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos); +int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len, + struct wpa_channel_info *ci, int tx_chanwidth, + int tx_seg1_idx); + +#endif /* OCV_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 7c75d0804553..c34a3bc1f3e4 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -42,8 +42,12 @@ enum qca_radiotap_vendor_ids { * * @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. + * co-existence information in the driver. These frequencies aim to + * minimize the traffic but not to totally avoid the traffic. That said + * for a P2P use case, these frequencies are allowed for the P2P + * discovery/negotiation but avoid the group to get formed on these + * frequencies. 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. @@ -499,6 +503,27 @@ enum qca_radiotap_vendor_ids { * * Based on the config provided, FW will boost the weight and prioritize * the traffic for that subsystem (WLAN/BT/Zigbee). + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query + * the supported AKM suite selectorss from the driver. It returns the list + * of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES. + * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware + * state from the driver. It returns the firmware state in the attribute + * QCA_WLAN_VENDOR_ATTR_FW_STATE. + * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand + * is used by the driver to flush per-peer cached statistics to user space + * application. This interface is used as an event from the driver to + * user space application. Attributes for this event are specified in + * enum qca_wlan_vendor_attr_peer_stats_cache_params. + * QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be + * sent in the event. + * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to + * improve the success rate of Zigbee joining network. + * Due to PTA master limitation, Zigbee joining network success rate is + * low while WLAN is working. The WLAN driver needs to configure some + * parameters including Zigbee state and specific WLAN periods to enhance + * PTA master. All these parameters are delivered by the attributes + * defined in enum qca_mpta_helper_vendor_attr. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -663,6 +688,10 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173, QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174, QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175, + QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176, + QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177, + QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178, + QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179, }; enum qca_wlan_vendor_attr { @@ -843,6 +872,18 @@ enum qca_wlan_vendor_attr { * to report the corresponding antenna index to the chain RSSI value */ QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report + * the specific antenna EVM value (unsigned 32 bit value). With a + * determinate group of antennas, the driver specifies the EVM value + * for each antenna ID, and application extract them in user space. + */ + QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41, + /* + * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report + * wlan firmware current state. FW state is an unsigned 8 bit value, + * one of the values in enum qca_wlan_vendor_attr_fw_state. + */ + QCA_WLAN_VENDOR_ATTR_FW_STATE = 42, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, @@ -854,6 +895,49 @@ enum qca_roaming_policy { QCA_ROAMING_ALLOWED_WITHIN_ESS, }; +/** + * enum qca_roam_reason - Represents the reason codes for roaming. Used by + * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON. + * + * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below + * reasons. + * + * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached + * the configured threshold. + * + * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured + * beacon misses from the then connected AP. + * + * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported + * by the connected AP. + * + * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better + * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor. + * + * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel + * or environment being very noisy or congested. + * + * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request + * from the user (user space). + * + * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from + * the connected AP. + * + * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization + * breaching out the configured threshold. + */ +enum qca_roam_reason { + QCA_ROAM_REASON_UNKNOWN, + QCA_ROAM_REASON_PER, + QCA_ROAM_REASON_BEACON_MISS, + QCA_ROAM_REASON_POOR_RSSI, + QCA_ROAM_REASON_BETTER_RSSI, + QCA_ROAM_REASON_CONGESTION, + QCA_ROAM_REASON_USER_TRIGGER, + QCA_ROAM_REASON_BTM, + QCA_ROAM_REASON_BSS_LOAD, +}; + enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, @@ -896,6 +980,11 @@ enum qca_wlan_vendor_attr_roam_auth { * doing subsequent ERP based connections in the same realm. */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13, + /* A 16-bit unsigned value representing the reasons for the roaming. + * Defined by enum qca_roam_reason. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14, + /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = @@ -994,6 +1083,7 @@ enum qca_wlan_vendor_acs_hw_mode { * only OCE STA-CFON functionalities. * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self * managed regulatory. + * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time). * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -1005,6 +1095,7 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5, QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6, QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7, + QCA_WLAN_VENDOR_FEATURE_TWT = 8, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -2437,6 +2528,18 @@ enum qca_wlan_vendor_attr_dmg_rf_sector_type { QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX }; +/** + * enum qca_wlan_vendor_attr_fw_state - State of firmware + * + * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state + * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active + */ +enum qca_wlan_vendor_attr_fw_state { + QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR, + QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE, + QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX +}; + /** * BRP antenna limit mode * @@ -3181,6 +3284,8 @@ enum qca_wlan_vendor_attr_roaming_config_params { QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20, + /* Flag attribute indicates this BSSID blacklist as a hint */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST, @@ -4433,6 +4538,27 @@ enum qca_wlan_vendor_attr_spectral_cap { * qca_wlan_vendor_spectral_scan_cap_hw_gen. */ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5, + /* Spectral bin scaling formula ID. u16 attribute. + * It uses values defined in enum + * qca_wlan_vendor_spectral_scan_cap_formula_id. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID = 6, + /* Spectral bin scaling param - low level offset. + * s16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_LOW_LEVEL_OFFSET = 7, + /* Spectral bin scaling param - high level offset. + * s16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HIGH_LEVEL_OFFSET = 8, + /* Spectral bin scaling param - RSSI threshold. + * s16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RSSI_THR = 9, + /* Spectral bin scaling param - default AGC max gain. + * u8 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX = @@ -4577,6 +4703,20 @@ enum qca_wlan_vendor_attr_flush_pending { QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1, }; +/** + * qca_wlan_vendor_spectral_scan_cap_formula_id: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID in the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the + * Spectral bin scaling formula ID. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING: No scaling + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED: AGC gain + * and RSSI threshold based formula. + */ +enum qca_wlan_vendor_spectral_scan_cap_formula_id { + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING = 0, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED = 1, +}; + /** * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative * RF Operating Parameter (RROP) information is available, and if so, at which @@ -4836,6 +4976,10 @@ enum qca_wlan_vendor_attr_offloaded_packets { QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR, /* Unsigned 32-bit value, in milli seconds */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD, + /* This optional unsigned 16-bit attribute is used for specifying + * ethernet protocol type. If not specified ethertype defaults to IPv4. + */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE, /* keep last */ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST, @@ -5457,6 +5601,54 @@ enum qca_wlan_he_om_ctrl_ch_bw { QCA_WLAN_HE_OM_CTRL_BW_160M = 3, }; +/** + * enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for + * HE operating mode control transmit request. These attributes are + * sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and + * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: Mandatory 8-bit unsigned value + * indicates the maximum number of spatial streams, NSS, that the STA + * supports in reception for PPDU bandwidths less than or equal to 80 MHz + * and is set to NSS - 1. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: Mandatory 8-bit unsigned value + * indicates the operating channel width supported by the STA for both + * reception and transmission. Uses enum qca_wlan_he_om_ctrl_ch_bw values. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: Mandatory 8-bit unsigned value + * indicates the all trigger based UL MU operations by the STA. + * 0 - UL MU operations are enabled by the STA. + * 1 - All triggered UL MU transmissions are suspended by the STA. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value + * indicates the maximum number of space-time streams, NSTS, that + * the STA supports in transmission and is set to NSTS - 1. + * + * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value + * combined with the UL MU Disable subfield and the recipient's setting + * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC + * capabilities to determine which HE TB PPDUs are possible by the + * STA to transmit. + * 0 - UL MU data operations are enabled by the STA. + * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable + * bit is not set, else UL MU Tx is suspended. + * + */ +enum qca_wlan_vendor_attr_he_omi_tx { + QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS = 1, + QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2, + QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3, + QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4, + QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX = + QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1, +}; + /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION */ @@ -5703,6 +5895,43 @@ enum qca_wlan_vendor_attr_wifi_test_config { */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32, + /* Nested attribute to indicate HE operating mode control field + * transmission. It contains operating mode control field Nss, + * channel bandwidth, Tx Nsts and UL MU disable attributes. + * These nested attributes are used to send HE operating mode control + * with configured values. + * Uses the enum qca_wlan_vendor_attr_he_omi_tx attributes. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX = 33, + + /* 8-bit unsigned value to configure +HTC_HE support to indicate the + * support for the reception of a frame that carries an HE variant + * HT Control field. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34, + + /* 8-bit unsigned value to configure VHT support in 2.4G band. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35, + + /* 8-bit unsigned value to configure HE testbed defaults. + * This attribute is used to configure the testbed device. + * 1-set the device HE capabilities to testbed defaults. + * 0-reset the device HE capabilities to supported config. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36, + + /* 8-bit unsigned value to configure TWT request support. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37, + /* keep last */ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = @@ -6185,13 +6414,35 @@ enum qca_coex_config_profiles { QCA_WIFI_SAP_CLASS_3_MGMT = 7, QCA_WIFI_SAP_DATA = 8, QCA_WIFI_SAP_ALL = 9, + QCA_WIFI_CASE_MAX = 31, /* 32 - 63 corresponds to BT */ QCA_BT_A2DP = 32, QCA_BT_BLE = 33, QCA_BT_SCO = 34, + QCA_BT_CASE_MAX = 63, /* 64 - 95 corresponds to Zigbee */ QCA_ZB_LOW = 64, - QCA_ZB_HIGH = 65 + QCA_ZB_HIGH = 65, + QCA_ZB_CASE_MAX = 95, + /* 0xff is default value if the u8 profile value is not set. */ + QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255 +}; + +/** + * enum qca_vendor_attr_coex_config_types - Coex configurations types. + * This enum defines the valid set of values of coex configuration types. These + * values may used by attribute + * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE. + * + * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the + * weights to default values. + * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config + * weights with configurability value. + */ +enum qca_vendor_attr_coex_config_types { + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1, + QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2, }; /** @@ -6222,6 +6473,80 @@ enum qca_vendor_attr_coex_config { QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1, }; +/** + * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config + * attributes + * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG + * + * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute. + * Indicate config type. + * The config types are 32-bit values from qca_vendor_attr_coex_config_types + * + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute. + * Indicate the Priority 1 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute. + * Indicate the Priority 2 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute. + * Indicate the Priority 3 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute. + * Indicate the Priority 4 profiles. + * The profiles are 8-bit values from enum qca_coex_config_profiles. + * In same priority level, maximum to 4 profiles can be set here. + * NOTE: + * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority + * arrangement: + * 1: In the same u32 attribute (priority x), the profiles enum values own + * same priority level. + * 2: 0xff is default value if the u8 profile value is not set. + * 3: max to 4 rules/profiles in same priority level. + * 4: max to 4 priority level (priority 1 - priority 4) + * 5: one priority level only supports one scenario from WLAN/BT/ZB, + * hybrid rules not support. + * 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will + * remain blank to reset all parameters. + * For example: + * + * If the attributes as follow: + * priority 1: + * ------------------------------------ + * | 0xff | 0 | 1 | 2 | + * ------------------------------------ + * priority 2: + * ------------------------------------- + * | 0xff | 0xff | 0xff | 32 | + * ------------------------------------- + * priority 3: + * ------------------------------------- + * | 0xff | 0xff | 0xff | 65 | + * ------------------------------------- + * then it means: + * 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION + * owns same priority level. + * 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION + * has priority over BT_A2DP and ZB_HIGH. + * 3: BT_A2DP has priority over ZB_HIGH. + */ + +enum qca_vendor_attr_coex_config_three_way { + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5, + + /* Keep last */ + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST, + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX = + QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1, +}; + /** * enum qca_wlan_vendor_attr_link_properties - Represent the link properties. * @@ -6244,4 +6569,144 @@ enum qca_wlan_vendor_attr_link_properties { QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1, }; +/** + * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type + * This enum defines the valid set of values of peer stats cache types. These + * values are used by attribute + * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics + * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics + * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn + * statistics + */ +enum qca_vendor_attr_peer_stats_cache_type { + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS, + QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS, + QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS, +}; + +/** + * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines + * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH + * Information in these attributes is used to flush peer rate statistics from + * the driver to user application. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute + * Indicate peer statistics cache type. + * The statistics types are 32-bit values from + * enum qca_vendor_attr_peer_stats_cache_type. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array + * of size 6 octets, representing the peer MAC address. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute + * containing buffer of statistics to send to application layer entity. + * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute + * representing a cookie for peer unique session. + */ +enum qca_wlan_vendor_attr_peer_stats_cache_params { + QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4, + + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST, + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX = + QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1 +}; + +/** + * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state + * This enum defines all the possible states of Zigbee, which can be + * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute. + * + * @ZIGBEE_IDLE: Zigbee in idle state + * @ZIGBEE_FORM_NETWORK: Zigbee forming network + * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network + * @ZIGBEE_JOIN: Zigbee joining network + * @ZIGBEE_NETWORK_UP: Zigbee network is up + * @ZIGBEE_HMI: Zigbee in HMI mode + */ +enum qca_mpta_helper_attr_zigbee_state { + ZIGBEE_IDLE = 0, + ZIGBEE_FORM_NETWORK = 1, + ZIGBEE_WAIT_JOIN = 2, + ZIGBEE_JOIN = 3, + ZIGBEE_NETWORK_UP = 4, + ZIGBEE_HMI = 5, +}; + +/* + * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command + * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG. + */ +enum qca_mpta_helper_vendor_attr { + QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0, + /* Optional attribute used to update Zigbee state. + * enum qca_mpta_helper_attr_zigbee_state. + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1, + /* Optional attribute used to configure WLAN duration for Shape-OCS + * during interrupt. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION. + * Value range 0 ~ 300 (ms). + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2, + /* Optional attribute used to configure non-WLAN duration for Shape-OCS + * during interrupt. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION. + * Value range 0 ~ 300 (ms). + * NLA_U32 attribute. + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3, + /* Optional attribute used to configure WLAN duration for Shape-OCS + * monitor period. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION. + * Value range 0 ~ 300 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4, + /* Optional attribute used to configure non-WLAN duration for Shape-OCS + * monitor period. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION. + * Value range 0 ~ 300 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5, + /* Optional attribute used to configure OCS interrupt duration. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION. + * Value range 1000 ~ 20000 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6, + /* Optional attribute used to configure OCS monitor duration. + * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION. + * Value range 1000 ~ 20000 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7, + /* Optional attribute used to notify WLAN firmware the current Zigbee + * channel. + * Value range 11 ~ 26 + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8, + /* Optional attribute used to configure WLAN mute duration. + * Value range 0 ~ 400 (ms) + * NLA_U32 attribute + */ + QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9, + + /* keep last */ + QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST, + QCA_MPTA_HELPER_VENDOR_ATTR_MAX = + QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1 +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 981e788dc751..5a50294a6dc8 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "utils/const_time.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" @@ -17,10 +18,33 @@ #include "sae.h" +static int sae_suitable_group(int group) +{ +#ifdef CONFIG_TESTING_OPTIONS + /* Allow all groups for testing purposes in non-production builds. */ + return 1; +#else /* CONFIG_TESTING_OPTIONS */ + /* Enforce REVmd rules on which SAE groups are suitable for production + * purposes: FFC groups whose prime is >= 3072 bits and ECC groups + * defined over a prime field whose prime is >= 256 bits. Furthermore, + * ECC groups defined over a characteristic 2 finite field and ECC + * groups with a co-factor greater than 1 are not suitable. */ + return group == 19 || group == 20 || group == 21 || + group == 28 || group == 29 || group == 30 || + group == 15 || group == 16 || group == 17 || group == 18; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + int sae_set_group(struct sae_data *sae, int group) { struct sae_temporary_data *tmp; + if (!sae_suitable_group(group)) { + wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); + return -1; + } + sae_clear_data(sae); tmp = sae->tmp = os_zalloc(sizeof(*tmp)); if (tmp == NULL) @@ -208,12 +232,14 @@ get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, static int is_quadratic_residue_blind(struct sae_data *sae, const u8 *prime, size_t bits, - const struct crypto_bignum *qr, - const struct crypto_bignum *qnr, + const u8 *qr, const u8 *qnr, const struct crypto_bignum *y_sqr) { - struct crypto_bignum *r, *num; + struct crypto_bignum *r, *num, *qr_or_qnr = NULL; int r_odd, check, res = -1; + u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN]; + size_t prime_len = sae->tmp->prime_len; + unsigned int mask; /* * Use the blinding technique to mask y_sqr while determining @@ -224,7 +250,7 @@ static int is_quadratic_residue_blind(struct sae_data *sae, * r = a random number between 1 and p-1, inclusive * num = (v * r * r) modulo p */ - r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); + r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd); if (!r) return -1; @@ -234,50 +260,51 @@ static int is_quadratic_residue_blind(struct sae_data *sae, crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) goto fail; - if (r_odd) { - /* - * num = (num * qr) module p - * LGR(num, p) = 1 ==> quadratic residue - */ - if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) - goto fail; - check = 1; - } else { - /* - * num = (num * qnr) module p - * LGR(num, p) = -1 ==> quadratic residue - */ - if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) - goto fail; - check = -1; - } + /* + * Need to minimize differences in handling different cases, so try to + * avoid branches and timing differences. + * + * If r_odd: + * num = (num * qr) module p + * LGR(num, p) = 1 ==> quadratic residue + * else: + * num = (num * qnr) module p + * LGR(num, p) = -1 ==> quadratic residue + */ + mask = const_time_is_zero(r_odd); + const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); + if (!qr_or_qnr || + crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0) + goto fail; + /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */ + check = const_time_select_int(mask, -1, 1); res = crypto_bignum_legendre(num, sae->tmp->prime); if (res == -2) { res = -1; goto fail; } - res = res == check; + /* branchless version of res = res == check + * (res is -1, 0, or 1; check is -1 or 1) */ + mask = const_time_eq(res, check); + res = const_time_select_int(mask, 1, 0); fail: crypto_bignum_deinit(num, 1); crypto_bignum_deinit(r, 1); + crypto_bignum_deinit(qr_or_qnr, 1); return res; } static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, - const u8 *prime, - const struct crypto_bignum *qr, - const struct crypto_bignum *qnr, - struct crypto_bignum **ret_x_cand) + const u8 *prime, const u8 *qr, const u8 *qnr, + u8 *pwd_value) { - u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; struct crypto_bignum *y_sqr, *x_cand; int res; size_t bits; - *ret_x_cand = NULL; - wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ @@ -286,7 +313,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, prime, sae->tmp->prime_len, pwd_value, bits) < 0) return -1; if (bits % 8) - buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); @@ -297,31 +324,27 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, if (!x_cand) return -1; y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); - if (!y_sqr) { - crypto_bignum_deinit(x_cand, 1); + crypto_bignum_deinit(x_cand, 1); + if (!y_sqr) return -1; - } res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); crypto_bignum_deinit(y_sqr, 1); - if (res <= 0) { - crypto_bignum_deinit(x_cand, 1); - return res; - } - - *ret_x_cand = x_cand; - return 1; + return res; } +/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided + * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ 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; + struct crypto_bignum *a, *b = NULL; + int res, is_val; + u8 pwd_value_valid; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); @@ -333,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 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; - } + /* Check whether pwd-value < p */ + res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, + sae->tmp->prime_len); + /* pwd-value >= p is invalid, so res is < 0 for the valid cases and + * the negative sign can be used to fill the mask for constant time + * selection */ + pwd_value_valid = const_time_fill_msb(res); + + /* If pwd-value >= p, force pwd-value to be < p and perform the + * calculations anyway to hide timing difference. The derived PWE will + * be ignored in that case. */ + pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); /* PWE = pwd-value^((p-1)/r) modulo p */ + res = -1; a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (!a) + goto fail; + /* This is an optimization based on the used group that does not depend + * on the password in any way, so it is fine to use separate branches + * for this step without constant time operations. */ if (sae->tmp->dh->safe_prime) { /* * r = (p-1)/2 for the group used here, so this becomes: @@ -356,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 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; - } + crypto_bignum_div(b, sae->tmp->order, b) < 0) + goto fail; } - if (a == NULL || b == NULL) - res = -1; - else - res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + if (!b) + goto fail; - crypto_bignum_deinit(a, 0); - crypto_bignum_deinit(b, 0); + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + if (res < 0) + goto fail; - if (res < 0) { - wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); - return -1; - } + /* There were no fatal errors in calculations, so determine the return + * value using constant time operations. We get here for number of + * invalid cases which are cleared here after having performed all the + * computation. PWE is valid if pwd-value was less than prime and + * PWE > 1. Start with pwd-value check first and then use constant time + * operations to clear res to 0 if PWE is 0 or 1. + */ + res = const_time_select_u8(pwd_value_valid, 1, 0); + is_val = crypto_bignum_is_zero(pwe); + res = const_time_select_u8(const_time_is_zero(is_val), res, 0); + is_val = crypto_bignum_is_one(pwe); + res = const_time_select_u8(const_time_is_zero(is_val), res, 0); - /* 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; +fail: + crypto_bignum_deinit(a, 1); + crypto_bignum_deinit(b, 1); + return res; } @@ -431,25 +468,32 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr[3]; size_t len[3]; size_t num_elem; - u8 dummy_password[32]; - size_t dummy_password_len; + u8 *dummy_password, *tmp_password; int pwd_seed_odd = 0; u8 prime[SAE_MAX_ECC_PRIME_LEN]; size_t prime_len; - struct crypto_bignum *x = NULL, *qr, *qnr; + struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; + u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; + u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; size_t bits; - int res; + int res = -1; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ - dummy_password_len = password_len; - if (dummy_password_len > sizeof(dummy_password)) - dummy_password_len = sizeof(dummy_password); - if (random_get_bytes(dummy_password, dummy_password_len) < 0) - return -1; + os_memset(x_bin, 0, sizeof(x_bin)); + + dummy_password = os_malloc(password_len); + tmp_password = os_malloc(password_len); + if (!dummy_password || !tmp_password || + random_get_bytes(dummy_password, password_len) < 0) + goto fail; prime_len = sae->tmp->prime_len; if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), prime_len) < 0) - return -1; + goto fail; bits = crypto_ec_prime_len_bits(sae->tmp->ec); /* @@ -457,8 +501,10 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * (qnr) modulo p for blinding purposes during the loop. */ if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, - &qr, &qnr) < 0) - return -1; + &qr, &qnr) < 0 || + crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || + crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) + goto fail; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); @@ -474,7 +520,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, */ sae_pwd_seed_key(addr1, addr2, addrs); - addr[0] = password; + addr[0] = tmp_password; len[0] = password_len; num_elem = 1; if (identifier) { @@ -491,9 +537,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, * attacks that attempt to determine the number of iterations required * in the loop. */ - for (counter = 1; counter <= k || !x; counter++) { + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; - struct crypto_bignum *x_cand; if (counter > 200) { /* This should not happen in practice */ @@ -501,40 +546,49 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, break; } - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); + const_time_select_bin(found, dummy_password, password, + password_len, tmp_password); if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, - prime, qr, qnr, &x_cand); + prime, qr_bin, qnr_bin, x_cand_bin); + const_time_select_bin(found, x_bin, x_cand_bin, prime_len, + x_bin); + pwd_seed_odd = const_time_select_u8( + found, pwd_seed_odd, + pwd_seed[SHA256_MAC_LEN - 1] & 0x01); + os_memset(pwd_seed, 0, sizeof(pwd_seed)); if (res < 0) goto fail; - if (res > 0 && !x) { - wpa_printf(MSG_DEBUG, - "SAE: Selected pwd-seed with counter %u", - counter); - x = x_cand; - pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; - os_memset(pwd_seed, 0, sizeof(pwd_seed)); + /* Need to minimize differences in handling res == 0 and 1 here + * to avoid differences in timing and instruction cache access, + * so use const_time_select_*() to make local copies of the + * values based on whether this loop iteration was the one that + * found the pwd-seed/x. */ - /* - * Use a dummy password for the following rounds, if - * any. - */ - addr[0] = dummy_password; - len[0] = dummy_password_len; - } else if (res > 0) { - crypto_bignum_deinit(x_cand, 1); - } + /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them + * (with res converted to 0/0xff) handles this in constant time. + */ + found |= res * 0xff; + wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", + res, found); } - if (!x) { + if (!found) { wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); res = -1; goto fail; } + x = crypto_bignum_init_set(x_bin, prime_len); + if (!x) { + res = -1; + goto fail; + } + if (!sae->tmp->pwe_ecc) sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); if (!sae->tmp->pwe_ecc) @@ -543,7 +597,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, res = crypto_ec_point_solve_y_coord(sae->tmp->ec, sae->tmp->pwe_ecc, x, pwd_seed_odd); - crypto_bignum_deinit(x, 1); if (res < 0) { /* * This should not happen since we already checked that there @@ -555,27 +608,48 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, fail: crypto_bignum_deinit(qr, 0); crypto_bignum_deinit(qnr, 0); + os_free(dummy_password); + bin_clear_free(tmp_password, password_len); + crypto_bignum_deinit(x, 1); + os_memset(x_bin, 0, sizeof(x_bin)); + os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); return res; } +static int sae_modp_group_require_masking(int group) +{ + /* Groups for which pwd-value is likely to be >= p frequently */ + return group == 22 || group == 23 || group == 24; +} + + static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, const char *identifier) { - u8 counter; + u8 counter, k, sel_counter = 0; u8 addrs[2 * ETH_ALEN]; const u8 *addr[3]; size_t len[3]; size_t num_elem; - int found = 0; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ + u8 mask; + struct crypto_bignum *pwe; + size_t prime_len = sae->tmp->prime_len * 8; + u8 *pwe_buf; - if (sae->tmp->pwe_ffc == NULL) { - sae->tmp->pwe_ffc = crypto_bignum_init(); - if (sae->tmp->pwe_ffc == NULL) - return -1; - } + crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); + sae->tmp->pwe_ffc = NULL; + + /* Allocate a buffer to maintain selected and candidate PWE for constant + * time selection. */ + pwe_buf = os_zalloc(prime_len * 2); + pwe = crypto_bignum_init(); + if (!pwe_buf || !pwe) + goto fail; wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); @@ -599,7 +673,9 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, len[num_elem] = sizeof(counter); num_elem++; - for (counter = 1; !found; counter++) { + k = sae_modp_group_require_masking(sae->group) ? 40 : 1; + + for (counter = 1; counter <= k || !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; int res; @@ -609,20 +685,37 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, break; } - wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, addr, len, pwd_seed) < 0) break; - res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); + res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); + /* res is -1 for fatal failure, 0 if a valid PWE was not found, + * or 1 if a valid PWE was found. */ if (res < 0) break; - if (res > 0) { - wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); - found = 1; - } + /* Store the candidate PWE into the second half of pwe_buf and + * the selected PWE in the beginning of pwe_buf using constant + * time selection. */ + if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, + prime_len) < 0) + break; + const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, + prime_len, pwe_buf); + sel_counter = const_time_select_u8(found, sel_counter, counter); + mask = const_time_eq_u8(res, 1); + found = const_time_select_u8(found, found, mask); } - return found ? 0 : -1; + if (!found) + goto fail; + + wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); + sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); +fail: + crypto_bignum_deinit(pwe, 1); + bin_clear_free(pwe_buf, prime_len * 2); + return sae->tmp->pwe_ffc ? 0 : -1; } @@ -1394,23 +1487,31 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); - if (sae->tmp == NULL) { + if (!sae->tmp || !sae->peer_commit_scalar || + !sae->tmp->own_commit_scalar) { wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); return -1; } - if (sae->tmp->ec) + if (sae->tmp->ec) { + if (!sae->tmp->peer_commit_element_ecc || + !sae->tmp->own_commit_element_ecc) + return -1; 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 + } else { + if (!sae->tmp->peer_commit_element_ffc || + !sae->tmp->own_commit_element_ffc) + return -1; 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"); diff --git a/src/common/sae.h b/src/common/sae.h index 3fbcb58d155a..3eb6e323a68f 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -40,6 +40,8 @@ struct sae_temporary_data { struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; char *pw_id; + int vlan_id; + u8 bssid[ETH_ALEN]; }; enum sae_state { diff --git a/src/common/version.h b/src/common/version.h index 2f47903d407c..06fc5e4d25a3 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.7" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.8" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 2d989048ac94..ed2d1c2a0236 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -340,14 +340,21 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy * PTK = PRF-X(PMK, "Pairwise key expansion", * Min(AA, SA) || Max(AA, SA) || - * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + * Min(ANonce, SNonce) || Max(ANonce, SNonce) + * [ || Z.x ]) + * + * The optional Z.x component is used only with DPP and that part is not defined + * in IEEE 802.11. */ 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) + struct wpa_ptk *ptk, int akmp, int cipher, + const u8 *z, size_t z_len) { - u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; +#define MAX_Z_LEN 66 /* with NIST P-521 */ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN]; + size_t data_len = 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; @@ -356,6 +363,9 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, return -1; } + if (z_len > MAX_Z_LEN) + return -1; + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { os_memcpy(data, addr1, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); @@ -374,6 +384,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, WPA_NONCE_LEN); } + if (z && z_len) { + os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len); + data_len += z_len; + } + ptk->kck_len = wpa_kck_len(akmp, pmk_len); ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); @@ -388,7 +403,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, if (wpa_key_mgmt_sha384(akmp)) { #if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); - if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; #else /* CONFIG_SUITEB192 || CONFIG_FILS */ @@ -397,7 +412,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) { #if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS) wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); - if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; #else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ @@ -406,17 +421,17 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #ifdef CONFIG_DPP } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); - if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); - if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); - if (sha512_prf(pmk, pmk_len, label, data, sizeof(data), + if (sha512_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } else if (akmp == WPA_KEY_MGMT_DPP) { @@ -426,7 +441,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, #endif /* CONFIG_DPP */ } else { wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)"); - if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, + if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp, ptk_len) < 0) return -1; } @@ -435,6 +450,8 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, MAC2STR(addr1), MAC2STR(addr2)); wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); + if (z && z_len) + wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len); @@ -451,6 +468,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, ptk->kck2_len = 0; os_memset(tmp, 0, sizeof(tmp)); + os_memset(data, 0, data_len); return 0; } @@ -880,6 +898,12 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, parse->igtk_len = len; break; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + case FTIE_SUBELEM_OCI: + parse->oci = pos; + parse->oci_len = len; + break; +#endif /* CONFIG_OCV */ default: wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id); break; @@ -1203,6 +1227,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, left = rsn_ie_len - 6; data->group_cipher = WPA_CIPHER_GTK_NOT_USED; + data->has_group = 1; data->key_mgmt = WPA_KEY_MGMT_OSEN; data->proto = WPA_PROTO_OSEN; } else { @@ -1224,6 +1249,7 @@ 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); + data->has_group = 1; if (!wpa_cipher_valid_group(data->group_cipher)) { wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x (%08x)", @@ -1249,6 +1275,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, "count %u left %u", __func__, count, left); return -4; } + if (count) + data->has_pairwise = 1; for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; @@ -1467,6 +1495,15 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, } +int wpa_default_rsn_cipher(int freq) +{ + if (freq > 56160) + return WPA_CIPHER_GCMP; /* DMG */ + + return WPA_CIPHER_CCMP; +} + + #ifdef CONFIG_IEEE80211R /** @@ -1754,7 +1791,7 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, os_memcpy(ptk->tk, tmp + offset, ptk->tk_len); offset += ptk->tk_len; os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len); - offset = ptk->kck2_len; + offset += ptk->kck2_len; os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 62617444058b..e83d6887a1cd 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -110,6 +110,7 @@ WPA_CIPHER_BIP_CMAC_256) #define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #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) @@ -148,7 +149,8 @@ WPA_CIPHER_BIP_CMAC_256) #define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) #define WPA_CAPABILITY_PBAC BIT(12) #define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) -/* B14-B15: Reserved */ +#define WPA_CAPABILITY_OCVC BIT(14) +/* B15: Reserved */ /* IEEE 802.11r */ @@ -326,6 +328,7 @@ struct rsn_ftie_sha384 { #define FTIE_SUBELEM_GTK 2 #define FTIE_SUBELEM_R0KH_ID 3 #define FTIE_SUBELEM_IGTK 4 +#define FTIE_SUBELEM_OCI 5 struct rsn_rdie { u8 id; @@ -344,7 +347,8 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, 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); + struct wpa_ptk *ptk, int akmp, int cipher, + const u8 *z, size_t z_len); int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, const u8 *snonce, const u8 *anonce, const u8 *dh_ss, size_t dh_ss_len, u8 *pmk, size_t *pmk_len); @@ -389,7 +393,9 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, struct wpa_ie_data { int proto; int pairwise_cipher; + int has_pairwise; int group_cipher; + int has_group; int key_mgmt; int capabilities; size_t num_pmkid; @@ -402,6 +408,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); +int wpa_default_rsn_cipher(int freq); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int akmp); @@ -451,6 +458,10 @@ struct wpa_ft_ies { size_t tie_len; const u8 *igtk; size_t igtk_len; +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ const u8 *ric; size_t ric_len; int key_mgmt; diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 623c2a768e43..c9890a0e4905 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -11,6 +11,8 @@ #ifdef CONFIG_CTRL_IFACE #ifdef CONFIG_CTRL_IFACE_UNIX +#include +#include #include #include #include @@ -133,6 +135,19 @@ struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, return NULL; } tries++; +#ifdef ANDROID + /* Set client socket file permissions so that bind() creates the client + * socket with these permissions and there is no need to try to change + * them with chmod() after bind() which would have potential issues with + * race conditions. These permissions are needed to make sure the server + * side (wpa_supplicant or hostapd) can reply to the control interface + * messages. + * + * The lchown() calls below after bind() are also part of the needed + * operations to allow the response to go through. Those are using the + * no-deference-symlinks version to avoid races. */ + fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +#endif /* ANDROID */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { if (errno == EADDRINUSE && tries < 2) { @@ -151,10 +166,9 @@ struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, } #ifdef ANDROID - chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); /* Set group even if we do not have privileges to change owner */ - chown(ctrl->local.sun_path, -1, AID_WIFI); - chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + lchown(ctrl->local.sun_path, -1, AID_WIFI); + lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); if (os_strncmp(ctrl_path, "@android:", 9) == 0) { if (socket_local_client_connect( @@ -540,7 +554,8 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) return res; - if (res > 0 && reply[0] == '<') { + if ((res > 0 && reply[0] == '<') || + (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) { /* This is an unsolicited message from * wpa_supplicant, not the reply to the * request. Use msg_cb to report this to the diff --git a/src/crypto/Makefile b/src/crypto/Makefile index ee93e41fbbb1..ab108daac835 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -62,7 +62,9 @@ LIB_OBJS += crypto_internal-modexp.o LIB_OBJS += crypto_internal-rsa.o LIB_OBJS += tls_internal.o LIB_OBJS += fips_prf_internal.o +ifndef TEST_FUZZ LIB_OBJS += random.o +endif libcrypto.a: $(LIB_OBJS) diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index 9fdb4f35cb9b..baeffcaf630c 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -99,6 +99,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; + + if (TEST_FAIL()) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 507b7cab86fc..12109ce83a9a 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -420,6 +420,7 @@ int __must_check crypto_public_key_decrypt_pkcs1( int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, u8 *pubkey); int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len); @@ -702,14 +703,6 @@ struct crypto_ec * crypto_ec_init(int group); */ void crypto_ec_deinit(struct crypto_ec *e); -/** - * crypto_ec_cofactor - Set the cofactor into the big number - * @e: EC context from crypto_ec_init() - * @cofactor: Cofactor of curve. - * Returns: 0 on success, -1 on failure - */ -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor); - /** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 7a797b5c359d..4ef11462b36e 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -310,12 +310,51 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + gcry_mpi_t pub = NULL; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_cmp_ui(pub, 1) <= 0) + goto fail; + + if (order) { + gcry_mpi_t p = NULL, q = NULL, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + tmp = gcry_mpi_new(prime_len * 8); + failed = !tmp || + gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len, + NULL) != GPG_ERR_NO_ERROR || + gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len, + NULL) != GPG_ERR_NO_ERROR; + if (!failed) { + gcry_mpi_powm(tmp, pub, q, p); + failed = gcry_mpi_cmp_ui(tmp, 1) != 0; + } + gcry_mpi_release(p); + gcry_mpi_release(q); + gcry_mpi_release(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + gcry_mpi_release(pub); + return res; } diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 92581ac676d3..6819f1a6ab6a 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -40,12 +40,49 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + struct bignum *pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + pub = bignum_init(); + if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 || + bignum_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + struct bignum *p, *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + p = bignum_init(); + q = bignum_init(); + tmp = bignum_init(); + failed = !p || !q || !tmp || + bignum_set_unsigned_bin(p, prime, prime_len) < 0 || + bignum_set_unsigned_bin(q, order, order_len) < 0 || + bignum_exptmod(pub, q, p, tmp) < 0 || + bignum_cmp_d(tmp, 1) != 0; + bignum_deinit(p); + bignum_deinit(q); + bignum_deinit(tmp); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + bignum_deinit(pub); + return res; } diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index d391f48ab5b1..aad40af16e06 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -310,6 +310,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return 0; } diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index 259f99500bcd..ed30efa021d7 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -278,6 +278,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) os_free(ctx); + if (TEST_FAIL()) + return -1; + return ret; } @@ -721,10 +724,12 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { + /* TODO: check pubkey */ return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, prime, prime_len, secret, len); } diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c index 8099193bf068..17244561b372 100644 --- a/src/crypto/crypto_linux.c +++ b/src/crypto/crypto_linux.c @@ -386,6 +386,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } crypto_hash_deinit(ctx); + + if (TEST_FAIL()) + return -1; return 0; } diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c index 4e31bc801132..f85d36532ea1 100644 --- a/src/crypto/crypto_nettle.c +++ b/src/crypto/crypto_nettle.c @@ -331,12 +331,44 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + mpz_t pub; + int res = -1; + + if (pubkey_len > prime_len || + (pubkey_len == prime_len && + os_memcmp(pubkey, prime, prime_len) >= 0)) + return -1; + + mpz_init(pub); + mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey); + if (mpz_cmp_d(pub, 1) <= 0) + goto fail; + + if (order) { + mpz_t p, q, tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + mpz_inits(p, q, tmp, NULL); + mpz_import(p, prime_len, 1, 1, 1, 0, prime); + mpz_import(q, order_len, 1, 1, 1, 0, order); + mpz_powm(tmp, pub, q, p); + failed = mpz_cmp_d(tmp, 1) != 0; + mpz_clears(p, q, tmp, NULL); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + mpz_clear(pub); + return res; } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index f89053a89d67..1b0c1ec96b36 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -24,6 +24,7 @@ #endif /* CONFIG_ECC */ #include "common.h" +#include "utils/const_time.h" #include "wpabuf.h" #include "dh_group5.h" #include "sha1.h" @@ -111,6 +112,31 @@ static BIGNUM * get_group5_prime(void) #endif } + +static BIGNUM * get_group5_order(void) +{ + static const unsigned char RFC3526_ORDER_1536[] = { + 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 + }; + return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL); +} + + #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif @@ -518,12 +544,45 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) { - return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, - prime, prime_len, secret, len); + BIGNUM *pub, *p; + int res = -1; + + pub = BN_bin2bn(pubkey, pubkey_len, NULL); + p = BN_bin2bn(prime, prime_len, NULL); + if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) || + BN_cmp(pub, p) >= 0) + goto fail; + + if (order) { + BN_CTX *ctx; + BIGNUM *q, *tmp; + int failed; + + /* verify: pubkey^q == 1 mod p */ + q = BN_bin2bn(order, order_len, NULL); + ctx = BN_CTX_new(); + tmp = BN_new(); + failed = !q || !ctx || !tmp || + !BN_mod_exp(tmp, pub, q, p, ctx) || + !BN_is_one(tmp); + BN_clear(q); + BN_clear(tmp); + BN_CTX_free(ctx); + if (failed) + goto fail; + } + + res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +fail: + BN_clear(pub); + BN_clear(p); + return res; } @@ -549,7 +608,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len, bn_result == NULL) goto error; - if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus, + ctx, NULL) != 1) goto error; *result_len = BN_bn2bin(bn_result, result); @@ -709,6 +769,10 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) if (dh->p == NULL) goto err; + dh->q = get_group5_order(); + if (!dh->q) + goto err; + if (DH_generate_key(dh) != 1) goto err; @@ -737,7 +801,7 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; - BIGNUM *p = NULL, *g; + BIGNUM *p, *g, *q; const BIGNUM *priv_key = NULL, *pub_key = NULL; *priv = NULL; @@ -750,10 +814,12 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) g = BN_new(); p = get_group5_prime(); - if (!g || BN_set_word(g, 2) != 1 || !p || - DH_set0_pqg(dh, p, NULL, g) != 1) + q = get_group5_order(); + if (!g || BN_set_word(g, 2) != 1 || !p || !q || + DH_set0_pqg(dh, p, q, g) != 1) goto err; p = NULL; + q = NULL; g = NULL; if (DH_generate_key(dh) != 1) @@ -778,6 +844,7 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) err: BN_free(p); + BN_free(q); BN_free(g); wpabuf_clear_free(pubkey); wpabuf_clear_free(privkey); @@ -987,6 +1054,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; + if (res == 1) { *len = mdlen; return 0; @@ -1250,6 +1320,8 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) { + if (TEST_FAIL()) + return -1; return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; } @@ -1295,8 +1367,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, 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); + res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a, + (const BIGNUM *) b, (const BIGNUM *) c, + bnctx, NULL); BN_CTX_free(bnctx); return res ? 0 : -1; @@ -1315,6 +1388,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifdef OPENSSL_IS_BORINGSSL + /* TODO: use BN_mod_inverse_blinded() ? */ +#else /* OPENSSL_IS_BORINGSSL */ + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1348,6 +1426,9 @@ int crypto_bignum_div(const struct crypto_bignum *a, bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; +#ifndef OPENSSL_IS_BORINGSSL + BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME); +#endif /* OPENSSL_IS_BORINGSSL */ res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, (const BIGNUM *) b, bnctx); BN_CTX_free(bnctx); @@ -1425,6 +1506,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, BN_CTX *bnctx; BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + unsigned int mask; if (TEST_FAIL()) return -2; @@ -1439,16 +1521,17 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, /* exp = (p-1) / 2 */ !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) || !BN_rshift1(exp, exp) || - !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p, - bnctx)) + !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp, + (const BIGNUM *) p, bnctx, NULL)) goto fail; - if (BN_is_word(tmp, 1)) - res = 1; - else if (BN_is_zero(tmp)) - res = 0; - else - res = -1; + /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use + * constant time selection to avoid branches here. */ + res = -1; + mask = const_time_eq(BN_is_word(tmp, 1), 1); + res = const_time_select_int(mask, 1, res); + mask = const_time_eq(BN_is_zero(tmp), 1); + res = const_time_select_int(mask, 0, res); fail: BN_clear_free(tmp); @@ -1553,13 +1636,6 @@ void crypto_ec_deinit(struct crypto_ec *e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor, - e->bnctx) == 0 ? -1 : 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c index b5a1e3fa31bc..976a008651b7 100644 --- a/src/crypto/crypto_wolfssl.c +++ b/src/crypto/crypto_wolfssl.c @@ -826,6 +826,7 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *order, size_t order_len, const u8 *privkey, size_t privkey_len, const u8 *pubkey, size_t pubkey_len, u8 *secret, size_t *len) @@ -952,6 +953,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) ret = 0; done: bin_clear_free(ctx, sizeof(*ctx)); + if (TEST_FAIL()) + return -1; return ret; } @@ -1082,6 +1085,8 @@ int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) int ret = 0; WC_RNG rng; + if (TEST_FAIL()) + return -1; if (wc_InitRng(&rng) != 0) return -1; if (mp_rand_prime((mp_int *) r, @@ -1347,16 +1352,6 @@ void crypto_ec_deinit(struct crypto_ec* e) } -int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) -{ - if (!e || !cofactor) - return -1; - - mp_set((mp_int *) cofactor, e->key.dp->cofactor); - return 0; -} - - struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index a9b770ec1f16..5e421b24f5a5 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1249,6 +1249,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, if (shared == NULL) return NULL; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dh->order, dh->order_len, wpabuf_head(own_private), wpabuf_len(own_private), wpabuf_head(peer_public), diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c index d9c737a2970b..cf408e84fae6 100644 --- a/src/crypto/md4-internal.c +++ b/src/crypto/md4-internal.c @@ -85,7 +85,7 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); (cp)[1] = (value) >> 8; \ (cp)[0] = (value); } while (0) -static u8 PADDING[MD4_BLOCK_LENGTH] = { +static const u8 PADDING[MD4_BLOCK_LENGTH] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/src/crypto/random.c b/src/crypto/random.c index c278d9cb9de9..1cabf3f4b9a4 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -25,6 +25,9 @@ #include "utils/includes.h" #ifdef __linux__ #include +#ifdef CONFIG_GETRANDOM +#include +#endif /* CONFIG_GETRANDOM */ #endif /* __linux__ */ #include "utils/common.h" @@ -228,30 +231,52 @@ int random_pool_ready(void) return 1; /* Already initialized - good to continue */ /* - * Try to fetch some more data from the kernel high quality - * /dev/random. There may not be enough data available at this point, + * Try to fetch some more data from the kernel high quality RNG. + * There may not be enough data available at this point, * so use non-blocking read to avoid blocking the application * completely. */ - fd = open("/dev/random", O_RDONLY | O_NONBLOCK); - if (fd < 0) { - wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(errno)); - return -1; + +#ifdef CONFIG_GETRANDOM + res = getrandom(dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK); + if (res < 0) { + if (errno == ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() not supported, falling back to /dev/random"); + } else { + wpa_printf(MSG_INFO, + "random: no data from getrandom(): %s", + strerror(errno)); + res = 0; + } + } +#else /* CONFIG_GETRANDOM */ + res = -1; +#endif /* CONFIG_GETRANDOM */ + if (res < 0) { + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot open /dev/random: %s", + strerror(errno)); + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, + "random: Cannot read from /dev/random: %s", + strerror(errno)); + res = 0; + } + close(fd); } - res = read(fd, dummy_key + dummy_key_avail, - sizeof(dummy_key) - dummy_key_avail); - if (res < 0) { - wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " - "%s", strerror(errno)); - res = 0; - } - wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " - "/dev/random", (unsigned) res, + wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res, (unsigned) (sizeof(dummy_key) - dummy_key_avail)); dummy_key_avail += res; - close(fd); if (dummy_key_avail == sizeof(dummy_key)) { if (own_pool_ready < MIN_READY_MARK) @@ -261,7 +286,7 @@ int random_pool_ready(void) } wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " - "random data available from /dev/random", + "random data available", (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); if (own_pool_ready >= MIN_READY_MARK || @@ -413,6 +438,19 @@ void random_init(const char *entropy_file) if (random_fd >= 0) return; +#ifdef CONFIG_GETRANDOM + { + u8 dummy; + + if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 || + errno != ENOSYS) { + wpa_printf(MSG_DEBUG, + "random: getrandom() support available"); + return; + } + } +#endif /* CONFIG_GETRANDOM */ + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c index f9bc0ebf6e3d..a11649a933eb 100644 --- a/src/crypto/sha1-tlsprf.c +++ b/src/crypto/sha1-tlsprf.c @@ -40,9 +40,6 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, const unsigned char *SHA1_addr[3]; size_t SHA1_len[3]; - if (secret_len & 1) - return -1; - MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c index 76c4fe750b65..c0263941c123 100644 --- a/src/crypto/sha512-internal.c +++ b/src/crypto/sha512-internal.c @@ -109,9 +109,14 @@ static const u64 K[80] = { /* compress 1024-bits */ static int sha512_compress(struct sha512_state *md, unsigned char *buf) { - u64 S[8], W[80], t0, t1; + u64 S[8], t0, t1; + u64 *W; int i; + W = os_malloc(80 * sizeof(u64)); + if (!W) + return -1; + /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->state[i]; @@ -146,6 +151,7 @@ static int sha512_compress(struct sha512_state *md, unsigned char *buf) md->state[i] = md->state[i] + S[i]; } + os_free(W); return 0; } diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c new file mode 100644 index 000000000000..66311c373920 --- /dev/null +++ b/src/crypto/sha512.c @@ -0,0 +1,104 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2003-2018, 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 "sha512.h" +#include "crypto.h" + + +/** + * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[64]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA512(key) */ + if (key_len > 128) { + if (sha512_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 64; + } + + /* the HMAC_SHA512 transform looks like: + * + * SHA512(K XOR opad, SHA512(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA512 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA512_MAC_LEN; + return sha512_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (64 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 481b34681d7b..8bdb91ff2469 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -42,6 +42,7 @@ enum tls_fail_reason { TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, + TLS_FAIL_DN_MISMATCH = 12, }; @@ -82,6 +83,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, @@ -103,6 +105,9 @@ struct tls_config { #define TLS_CONN_SUITEB BIT(11) #define TLS_CONN_SUITEB_NO_ECDH BIT(12) #define TLS_CONN_DISABLE_TLSv1_3 BIT(13) +#define TLS_CONN_ENABLE_TLSv1_0 BIT(14) +#define TLS_CONN_ENABLE_TLSv1_1 BIT(15) +#define TLS_CONN_ENABLE_TLSv1_2 BIT(16) /** * struct tls_connection_params - Parameters for TLS connection @@ -115,12 +120,19 @@ 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. + * @suffix_match: Semicolon deliminated string of values to suffix match against + * the dNSName or CN of the peer certificate or %NULL to allow all domain names. + * This may allow subdomains and wildcard certificates. Each domain name label + * must have a full case-insensitive 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. + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. * @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 @@ -144,12 +156,15 @@ struct tls_config { * @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 + * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if + * supported, empty string to disable, or a colon-separated curve list. * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if * ocsp_multi is not enabled + * @check_cert_subject: Client certificate subject name matching string * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -187,10 +202,12 @@ struct tls_connection_params { const char *cert_id; const char *ca_cert_id; const char *openssl_ciphers; + const char *openssl_ecdh_curves; unsigned int flags; const char *ocsp_stapling_response; const char *ocsp_stapling_response_multi; + const char *check_cert_subject; }; @@ -321,9 +338,11 @@ int __must_check tls_global_set_params( * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates + * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, + int strict); /** * tls_connection_set_verify - Set certificate verification options @@ -358,15 +377,21 @@ int __must_check tls_connection_get_random(void *tls_ctx, * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * Exports keying material using the mechanism described in RFC 5705. + * Exports keying material using the mechanism described in RFC 5705. If + * context is %NULL, context is not provided; otherwise, context is provided + * (including the case of empty context with context_len == 0). */ int __must_check tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, const char *label, + const u8 *context, + size_t context_len, u8 *out, size_t out_len); /** diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 36dafd2603f0..daa01d9ed4f6 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -461,6 +461,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ecdh_curves not supported"); + return -1; + } + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ @@ -733,6 +739,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; int ret; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -842,7 +851,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { /* TODO */ return 0; @@ -889,14 +898,23 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (conn == NULL || conn->session == NULL) return -1; +#if GNUTLS_VERSION_NUMBER >= 0x030404 + return gnutls_prf_rfc5705(conn->session, os_strlen(label), label, + context_len, (const char *) context, + out_len, (char *) out); +#else /* 3.4.4 */ + if (context) + return -1; return gnutls_prf(conn->session, os_strlen(label), label, 0 /* client_random first */, 0, NULL, out_len, (char *) out); +#endif /* 3.4.4 */ } @@ -1068,6 +1086,52 @@ static int check_ocsp(struct tls_connection *conn, gnutls_session_t session, } +static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match, + int full) +{ + int res = -1; + +#if GNUTLS_VERSION_NUMBER >= 0x030300 + if (full) + res = gnutls_x509_crt_check_hostname2( + cert, match, + GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS); +#endif /* >= 3.3.0 */ + if (res == -1) + res = gnutls_x509_crt_check_hostname(cert, match); + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d", + full ? "": "suffix ", match, res); + return res; +} + + +static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match, + int full) +{ + char *values, *token, *context = NULL; + int ret = 0; + + if (!os_strchr(match, ';')) + return tls_match_suffix_helper(cert, match, full); + + values = os_strdup(match); + if (!values) + return 0; + + /* Process each match alternative separately until a match is found */ + while ((token = str_token(values, ";", &context))) { + if (tls_match_suffix_helper(cert, token, full)) { + ret = 1; + break; + } + } + + os_free(values); + return ret; +} + + static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; @@ -1263,8 +1327,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) if (i == 0) { if (conn->suffix_match && - !gnutls_x509_crt_check_hostname( - cert, conn->suffix_match)) { + !tls_match_suffix(cert, conn->suffix_match, 0)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); @@ -1280,9 +1343,7 @@ static int tls_connection_verify_peer(gnutls_session_t session) #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && - !gnutls_x509_crt_check_hostname2( - cert, conn->domain_match, - GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { + !tls_match_suffix(cert, conn->domain_match, 1)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index d289c9442ceb..8095b43bd21b 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -248,6 +248,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported"); + tlsv1_cred_free(cred); + return -1; + } + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { @@ -303,6 +309,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -353,7 +362,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { struct tls_global *global = tls_ctx; global->check_crl = check_crl; @@ -403,7 +412,8 @@ static int tls_get_keyblock_size(struct tls_connection *conn) static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, + const char *label, const u8 *context, + size_t context_len, int server_random_first, int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; @@ -422,15 +432,15 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - ret = tlsv1_client_prf(conn->client, label, - server_random_first, + ret = tlsv1_client_prf(conn->client, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - ret = tlsv1_server_prf(conn->server, label, - server_random_first, + ret = tlsv1_server_prf(conn->server, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ @@ -443,17 +453,19 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len); + return tls_connection_prf(tls_ctx, conn, label, context, context_len, + 0, 0, out, out_len); } int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, u8 *out, size_t out_len) { - return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out, - out_len); + return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0, + 1, 1, out, out_len); } @@ -720,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_failed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_read_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } @@ -733,6 +753,10 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_write_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index 5d0c6bda1547..6d6fb0cafd31 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -72,7 +72,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { return -1; } @@ -94,7 +94,8 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { return -1; } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 0d5ebda699f0..b0c23ae6c9b1 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -104,7 +104,9 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #ifdef CONFIG_SUITEB static int RSA_bits(const RSA *r) { @@ -212,10 +214,17 @@ static struct tls_context *tls_global = NULL; struct tls_data { SSL_CTX *ssl; unsigned int tls_session_lifetime; + int check_crl; + int check_crl_strict; + char *ca_cert; + unsigned int crl_reload_interval; + struct os_reltime crl_last_reload; + char *check_cert_subject; }; struct tls_connection { struct tls_context *context; + struct tls_data *data; SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; @@ -224,6 +233,7 @@ struct tls_connection { EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ char *subject_match, *altsubject_match, *suffix_match, *domain_match; + char *check_cert_subject; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -301,6 +311,36 @@ static void tls_show_errors(int level, const char *func, const char *txt) #endif /* CONFIG_NO_STDOUT_DEBUG */ +static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl) +{ + int flags; + X509_STORE *store; + + store = X509_STORE_new(); + if (!store) { + wpa_printf(MSG_DEBUG, + "OpenSSL: %s - failed to allocate new certificate store", + __func__); + return NULL; + } + + if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + X509_STORE_free(store); + return NULL; + } + + flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + + X509_STORE_set_flags(store, flags); + + return store; +} + + #ifdef CONFIG_NATIVE_WINDOWS /* Windows CryptoAPI and access to certificate stores */ @@ -989,8 +1029,10 @@ void * tls_init(const struct tls_config *conf) return NULL; } data->ssl = ssl; - if (conf) + if (conf) { data->tls_session_lifetime = conf->tls_session_lifetime; + data->crl_reload_interval = conf->crl_reload_interval; + } SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); @@ -1072,6 +1114,7 @@ void tls_deinit(void *ssl_ctx) os_free(context); if (data->tls_session_lifetime > 0) SSL_CTX_flush_sessions(ssl, 0); + os_free(data->ca_cert); SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -1093,6 +1136,7 @@ void tls_deinit(void *ssl_ctx) tls_global = NULL; } + os_free(data->check_cert_subject); os_free(data); } @@ -1305,8 +1349,16 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "client hello"; case 2: return "server hello"; + case 3: + return "hello verify request"; case 4: return "new session ticket"; + case 5: + return "end of early data"; + case 6: + return "hello retry request"; + case 8: + return "encrypted extensions"; case 11: return "certificate"; case 12: @@ -1325,6 +1377,12 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, return "certificate url"; case 22: return "certificate status"; + case 23: + return "supplemental data"; + case 24: + return "key update"; + case 254: + return "message hash"; default: return "?"; } @@ -1467,11 +1525,32 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + X509_STORE *new_cert_store; + struct os_reltime now; struct tls_context *context = SSL_CTX_get_app_data(ssl); + /* Replace X509 store if it is time to update CRL. */ + if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &data->crl_last_reload, + data->crl_reload_interval)) { + wpa_printf(MSG_INFO, + "OpenSSL: Flushing X509 store with ca_cert file"); + new_cert_store = tls_crl_cert_reload(data->ca_cert, + data->check_crl); + if (!new_cert_store) { + wpa_printf(MSG_ERROR, + "OpenSSL: Error replacing X509 store with ca_cert file"); + } else { + /* Replace old store */ + SSL_CTX_set_cert_store(ssl, new_cert_store); + data->crl_last_reload = now; + } + } + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->data = data; conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { @@ -1535,6 +1614,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->domain_match); + os_free(conn->check_cert_subject); os_free(conn->session_ticket); os_free(conn); } @@ -1655,9 +1735,9 @@ 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 match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -1667,7 +1747,6 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -1687,12 +1766,223 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match, #endif /* CONFIG_NATIVE_WINDOWS */ -static int tls_match_suffix(X509 *cert, const char *match, int full) +struct tls_dn_field_order_cnt { + u8 cn; + u8 c; + u8 l; + u8 st; + u8 o; + u8 ou; + u8 email; +}; + + +static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt, + int nid) +{ + switch (nid) { + case NID_commonName: + return dn_cnt->cn; + case NID_countryName: + return dn_cnt->c; + case NID_localityName: + return dn_cnt->l; + case NID_stateOrProvinceName: + return dn_cnt->st; + case NID_organizationName: + return dn_cnt->o; + case NID_organizationalUnitName: + return dn_cnt->ou; + case NID_pkcs9_emailAddress: + return dn_cnt->email; + default: + wpa_printf(MSG_ERROR, + "TLS: Unknown NID '%d' in check_cert_subject", + nid); + return -1; + } +} + + +/** + * match_dn_field - Match configuration DN field against Certificate DN field + * @cert: Certificate + * @nid: NID of DN field + * @field: Field name + * @value DN field value which is passed from configuration + * e.g., if configuration have C=US and this argument will point to US. + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int match_dn_field(const X509 *cert, int nid, const char *field, + const char *value, + const struct tls_dn_field_order_cnt *dn_cnt) +{ + int i, ret = 0, len, config_dn_field_index, match_index = 0; + X509_NAME *name; + + len = os_strlen(value); + name = X509_get_subject_name((X509 *) cert); + + /* Assign incremented cnt for every field of DN to check DN field in + * right order */ + config_dn_field_index = get_dn_field_index(dn_cnt, nid); + if (config_dn_field_index < 0) + return 0; + + /* Fetch value based on NID */ + for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + e = X509_NAME_get_entry(name, i); + if (!e) + continue; + + cn = X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + + match_index++; + + /* check for more than one DN field with same name */ + if (match_index != config_dn_field_index) + continue; + + /* Check wildcard at the right end side */ + /* E.g., if OU=develop* mentioned in configuration, allow 'OU' + * of the subject in the client certificate to start with + * 'develop' */ + if (len > 0 && value[len - 1] == '*') { + /* Compare actual certificate DN field value with + * configuration DN field value up to the specified + * length. */ + ret = ASN1_STRING_length(cn) >= len - 1 && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len - 1) == 0; + } else { + /* Compare actual certificate DN field value with + * configuration DN field value */ + ret = ASN1_STRING_length(cn) == len && + os_memcmp(ASN1_STRING_get0_data(cn), value, + len) == 0; + } + if (!ret) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'", + field, value, ASN1_STRING_get0_data(cn)); + } + break; + } + + return ret; +} + + +/** + * get_value_from_field - Get value from DN field + * @cert: Certificate + * @field_str: DN field string which is passed from configuration file (e.g., + * C=US) + * @dn_cnt: DN matching context + * Returns: 1 on success and 0 on failure + */ +static int get_value_from_field(const X509 *cert, char *field_str, + struct tls_dn_field_order_cnt *dn_cnt) +{ + int nid; + char *context = NULL, *name, *value; + + if (os_strcmp(field_str, "*") == 0) + return 1; /* wildcard matches everything */ + + name = str_token(field_str, "=", &context); + if (!name) + return 0; + + /* Compare all configured DN fields and assign nid based on that to + * fetch correct value from certificate subject */ + if (os_strcmp(name, "CN") == 0) { + nid = NID_commonName; + dn_cnt->cn++; + } else if(os_strcmp(name, "C") == 0) { + nid = NID_countryName; + dn_cnt->c++; + } else if (os_strcmp(name, "L") == 0) { + nid = NID_localityName; + dn_cnt->l++; + } else if (os_strcmp(name, "ST") == 0) { + nid = NID_stateOrProvinceName; + dn_cnt->st++; + } else if (os_strcmp(name, "O") == 0) { + nid = NID_organizationName; + dn_cnt->o++; + } else if (os_strcmp(name, "OU") == 0) { + nid = NID_organizationalUnitName; + dn_cnt->ou++; + } else if (os_strcmp(name, "emailAddress") == 0) { + nid = NID_pkcs9_emailAddress; + dn_cnt->email++; + } else { + wpa_printf(MSG_ERROR, + "TLS: Unknown field '%s' in check_cert_subject", name); + return 0; + } + + value = str_token(field_str, "=", &context); + if (!value) { + wpa_printf(MSG_ERROR, + "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject", + name); + return 0; + } + + return match_dn_field(cert, nid, name, value, dn_cnt); +} + + +/** + * tls_match_dn_field - Match subject DN field with check_cert_subject + * @cert: Certificate + * @match: check_cert_subject string + * Returns: Return 1 on success and 0 on failure +*/ +static int tls_match_dn_field(X509 *cert, const char *match) +{ + const char *token, *last = NULL; + char field[256]; + struct tls_dn_field_order_cnt dn_cnt; + + os_memset(&dn_cnt, 0, sizeof(dn_cnt)); + + /* Maximum length of each DN field is 255 characters */ + + /* Process each '/' delimited field */ + while ((token = cstr_token(match, "/", &last))) { + if (last - token >= (int) sizeof(field)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Too long DN matching field value in '%s'", + match); + return 0; + } + os_memcpy(field, token, last - token); + field[last - token] = '\0'; + + if (!get_value_from_field(cert, field, &dn_cnt)) { + wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'", + field); + return 0; + } + } + + return 1; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static int tls_match_suffix_helper(X509 *cert, const char *match, + size_t match_len, 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; @@ -1714,8 +2004,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) gen->d.dNSName->data, gen->d.dNSName->length); if (domain_suffix_match(gen->d.dNSName->data, - gen->d.dNSName->length, match, full) == - 1) { + gen->d.dNSName->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); @@ -1746,8 +2036,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -1757,6 +2047,25 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", full ? "": "suffix "); 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 */ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; #endif /* CONFIG_NATIVE_WINDOWS */ } @@ -1951,6 +2260,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) struct tls_connection *conn; struct tls_context *context; char *match, *altmatch, *suffix_match, *domain_match; + const char *check_cert_subject; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -1991,6 +2301,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "time mismatch"); preverify_ok = 1; } + if (!preverify_ok && !conn->data->check_crl_strict && + (err == X509_V_ERR_CRL_HAS_EXPIRED || + err == X509_V_ERR_CRL_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Ignore certificate validity CRL time mismatch"); + preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); @@ -2044,6 +2361,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", preverify_ok, err, err_str, conn->ca_cert_verify, depth, buf); + check_cert_subject = conn->check_cert_subject; + if (!check_cert_subject) + check_cert_subject = conn->data->check_cert_subject; + if (check_cert_subject) { + if (depth == 0 && + !tls_match_dn_field(err_cert, check_cert_subject)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Distinguished Name", + TLS_FAIL_DN_MISMATCH); + } + } if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); @@ -2381,13 +2710,16 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) SSL_CTX_set_client_CA_list(ssl_ctx, SSL_load_client_CA_file(ca_cert)); #endif /* OPENSSL_NO_STDIO */ + + os_free(data->ca_cert); + data->ca_cert = os_strdup(ca_cert); } return 0; } -int tls_global_set_verify(void *ssl_ctx, int check_crl) +int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict) { int flags; @@ -2404,6 +2736,10 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) if (check_crl == 2) flags |= X509_V_FLAG_CRL_CHECK_ALL; X509_STORE_set_flags(cs, flags); + + data->check_crl = check_crl; + data->check_crl_strict = strict; + os_get_reltime(&data->crl_last_reload); } return 0; } @@ -2413,7 +2749,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match, const char *suffix_match, - const char *domain_match) + const char *domain_match, + const char *check_cert_subject) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -2447,6 +2784,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->check_cert_subject); + conn->check_cert_subject = NULL; + if (check_cert_subject) { + conn->check_cert_subject = os_strdup(check_cert_subject); + if (!conn->check_cert_subject) + return -1; + } + return 0; } @@ -2519,6 +2864,38 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, else SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3); #endif /* SSL_OP_NO_TLSv1_3 */ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (flags & (TLS_CONN_ENABLE_TLSv1_0 | + TLS_CONN_ENABLE_TLSv1_1 | + TLS_CONN_ENABLE_TLSv1_2)) { + int version = 0; + + /* Explicit request to enable TLS versions even if needing to + * override systemwide policies. */ + if (flags & TLS_CONN_ENABLE_TLSv1_0) { + version = TLS1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_1) { + if (!(flags & TLS_CONN_DISABLE_TLSv1_0)) + version = TLS1_1_VERSION; + } else if (flags & TLS_CONN_ENABLE_TLSv1_2) { + if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1))) + version = TLS1_2_VERSION; + } + if (!version) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid TLS version configuration"); + return -1; + } + + if (SSL_set_min_proto_version(ssl, version) != 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Failed to set minimum TLS version"); + return -1; + } + } +#endif /* >= 1.1.0 */ + #ifdef CONFIG_SUITEB #ifdef OPENSSL_IS_BORINGSSL /* Start with defaults from BoringSSL */ @@ -2621,7 +2998,22 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, return -1; } } +#else /* OPENSSL_IS_BORINGSSL */ + if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) && + openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } #endif /* OPENSSL_IS_BORINGSSL */ +#else /* CONFIG_SUITEB */ + if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set openssl_ciphers '%s'", + openssl_ciphers); + return -1; + } #endif /* CONFIG_SUITEB */ return 0; @@ -2743,6 +3135,15 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) + if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) { + ERR_clear_error(); + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file" + " --> OK"); + return 0; + } +#else if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { ERR_clear_error(); @@ -2750,6 +3151,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, " --> OK"); return 0; } +#endif tls_show_errors(MSG_DEBUG, __func__, "SSL_use_certificate_file failed"); @@ -3523,11 +3925,13 @@ static int openssl_get_keyblock_size(SSL *ssl) int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { if (!conn || SSL_export_keying_material(conn->ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) != 1) + os_strlen(label), context, context_len, + context != NULL) != 1) return -1; return 0; } @@ -4445,7 +4849,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->subject_match, params->altsubject_match, params->suffix_match, - params->domain_match)) + params->domain_match, + params->check_cert_subject)) return -1; if (engine_id && ca_cert_id) { @@ -4503,6 +4908,40 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC + if (SSL_set1_curves_list(conn->ssl, + params->openssl_ecdh_curves) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + if (tls_set_conn_flags(conn, params->flags, params->openssl_ciphers) < 0) return -1; @@ -4552,6 +4991,15 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } + os_free(data->check_cert_subject); + data->check_cert_subject = NULL; + if (params->check_cert_subject) { + data->check_cert_subject = + os_strdup(params->check_cert_subject); + if (!data->check_cert_subject) + return -1; + } + if (tls_global_ca_cert(data, params->ca_cert) || tls_global_client_cert(data, params->client_cert) || tls_global_private_key(data, params->private_key, @@ -4569,6 +5017,44 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (!params->openssl_ecdh_curves) { +#ifndef OPENSSL_IS_BORINGSSL +#ifndef OPENSSL_NO_EC +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) + if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves to auto"); + return -1; + } +#endif /* >= 1.0.2 && < 1.1.0 */ +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } else if (params->openssl_ecdh_curves[0]) { +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L) + wpa_printf(MSG_INFO, + "OpenSSL: ECDH configuration nnot supported"); + return -1; +#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */ +#ifndef OPENSSL_NO_EC +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_ecdh_auto(ssl_ctx, 1); +#endif + if (SSL_CTX_set1_curves_list(ssl_ctx, + params->openssl_ecdh_curves) != + 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH curves '%s'", + params->openssl_ecdh_curves); + return -1; + } +#else /* OPENSSL_NO_EC */ + wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported"); + return -1; +#endif /* OPENSSL_NO_EC */ +#endif /* OPENSSL_IS_BORINGSSL */ + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c index cc8c704466d2..e9cb425c115a 100644 --- a/src/crypto/tls_wolfssl.c +++ b/src/crypto/tls_wolfssl.c @@ -643,9 +643,9 @@ static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) static int domain_suffix_match(const char *val, size_t len, const char *match, - int full) + size_t match_len, int full) { - size_t i, match_len; + size_t i; /* Check for embedded nuls that could mess up suffix matching */ for (i = 0; i < len; i++) { @@ -656,7 +656,6 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } } - match_len = os_strlen(match); if (match_len > len || (full && match_len != len)) return 0; @@ -674,7 +673,8 @@ static int domain_suffix_match(const char *val, size_t len, const char *match, } -static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match, + size_t match_len, int full) { WOLFSSL_ASN1_OBJECT *gen; void *ext; @@ -690,14 +690,14 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { gen = wolfSSL_sk_value(ext, j); - if (gen->type != ALT_NAMES_OID) + if (gen->type != ASN_DNS_TYPE) continue; dns_name++; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", gen->obj, os_strlen((char *)gen->obj)); if (domain_suffix_match((const char *) gen->obj, os_strlen((char *) gen->obj), match, - full) == 1) { + match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); wolfSSL_sk_ASN1_OBJECT_free(ext); @@ -729,8 +729,8 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) continue; wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", cn->data, cn->length); - if (domain_suffix_match(cn->data, cn->length, match, full) == 1) - { + if (domain_suffix_match(cn->data, cn->length, + match, match_len, full) == 1) { wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", full ? "Match" : "Suffix match"); return 1; @@ -743,6 +743,20 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) } +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + const char *token, *last = NULL; + + /* Process each match alternative separately until a match is found */ + while ((token = cstr_token(match, ";", &last))) { + if (tls_match_suffix_helper(cert, token, last - token, full)) + return 1; + } + + return 0; +} + + static enum tls_fail_reason wolfssl_tls_fail_reason(int err) { switch (err) { @@ -1487,6 +1501,9 @@ int tls_global_set_params(void *tls_ctx, { wpa_printf(MSG_DEBUG, "SSL: global set params"); + if (params->check_cert_subject) + return -1; /* not yet supported */ + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", params->ca_cert); @@ -1524,6 +1541,12 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, + "wolfSSL: openssl_ecdh_curves not supported"); + return -1; + } + #ifdef HAVE_SESSION_TICKET /* Session ticket is off by default - can't disable once on. */ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) @@ -1543,7 +1566,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl); @@ -1964,8 +1987,11 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, - const char *label, u8 *out, size_t out_len) + const char *label, const u8 *context, + size_t context_len, u8 *out, size_t out_len) { + if (context) + return -1; if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) return -1; return 0; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 4ac9f16a0efc..e7c8f318f35d 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -58,6 +58,16 @@ #define HOSTAPD_CHAN_VHT_130_30 0x04000000 #define HOSTAPD_CHAN_VHT_150_10 0x08000000 +/* Allowed bandwidth mask */ +enum hostapd_chan_width_attr { + HOSTAPD_CHAN_WIDTH_10 = BIT(0), + HOSTAPD_CHAN_WIDTH_20 = BIT(1), + HOSTAPD_CHAN_WIDTH_40P = BIT(2), + HOSTAPD_CHAN_WIDTH_40M = BIT(3), + HOSTAPD_CHAN_WIDTH_80 = BIT(4), + HOSTAPD_CHAN_WIDTH_160 = BIT(5), +}; + /* Filter gratuitous ARP */ #define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0) /* Filter unsolicited Neighbor Advertisement */ @@ -110,6 +120,13 @@ struct hostapd_channel_data { */ int flag; + /** + * allowed_bw - Allowed channel width bitmask + * + * See enum hostapd_chan_width_attr. + */ + u32 allowed_bw; + /** * max_tx_power - Regulatory transmit power limit in dBm */ @@ -914,10 +931,10 @@ struct wpa_driver_associate_params { * passphrase - RSN passphrase for PSK * * This value is made available only for WPA/WPA2-Personal (PSK) and - * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is - * the 8..63 character ASCII passphrase, if available. Please note that - * this can be %NULL if passphrase was not used to generate the PSK. In - * that case, the psk field must be used to fetch the PSK. + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This + * is the 8..63 character ASCII passphrase, if available. Please note + * that this can be %NULL if passphrase was not used to generate the + * PSK. In that case, the psk field must be used to fetch the PSK. */ const char *passphrase; @@ -925,9 +942,9 @@ struct wpa_driver_associate_params { * psk - RSN PSK (alternative for passphrase for PSK) * * This value is made available only for WPA/WPA2-Personal (PSK) and - * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is - * the 32-octet (256-bit) PSK, if available. The driver wrapper should - * be prepared to handle %NULL value as an error. + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This + * is the 32-octet (256-bit) PSK, if available. The driver wrapper + * should be prepared to handle %NULL value as an error. */ const u8 *psk; @@ -1367,6 +1384,23 @@ struct wpa_driver_ap_params { * service). */ int multicast_to_unicast; + + /** + * ftm_responder - Whether FTM responder is enabled + */ + int ftm_responder; + + /** + * lci - Binary data, the content of an LCI report IE with type 8 as + * defined in IEEE Std 802.11-2016, 9.4.2.22.10 + */ + const struct wpabuf *lci; + + /** + * civic - Binary data, the content of a measurement report IE with + * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13 + */ + const struct wpabuf *civic; }; struct wpa_driver_mesh_bss_params { @@ -1424,6 +1458,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000 +#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000 /** Bitfield of supported key management suites */ unsigned int key_mgmt; @@ -1457,7 +1492,7 @@ struct wpa_driver_capa { #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 +#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008 /** Driver is for a wired Ethernet interface */ #define WPA_DRIVER_FLAGS_WIRED 0x00000010 /** Driver provides separate commands for authentication and association (SME in @@ -1579,6 +1614,10 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL /** Driver is a self-managed regulatory device */ #define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL +/** Driver supports FTM responder functionality */ +#define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL +/** Driver support 4-way handshake offload for WPA-Personal */ +#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL u64 flags; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ @@ -1902,17 +1941,6 @@ enum smps_mode { SMPS_INVALID, }; -/* 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 -}; - #define WPA_INVALID_NOISE 9999 /** @@ -1942,6 +1970,26 @@ struct wpa_signal_info { int center_frq2; }; +/** + * struct wpa_channel_info - Information about the current channel + * @frequency: Center frequency of the primary 20 MHz channel + * @chanwidth: Width of the current operating channel + * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1). + * This field is only filled in when using a 40 MHz channel. + * @center_frq1: Center frequency of frequency segment 0 + * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels) + * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is + * derived from center_frq2 for convenience. + */ +struct wpa_channel_info { + u32 frequency; + enum chan_width chanwidth; + int sec_channel; + int center_frq1; + int center_frq2; + u8 seg1_idx; +}; + /** * struct beacon_data - Beacon data * @head: Head portion of Beacon frame (before TIM IE) @@ -2108,17 +2156,19 @@ enum wpa_drv_update_connect_params_mask { * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give * the real status code for failures. Used only for the request interface * from user space to the driver. + * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE). */ struct external_auth { enum { EXT_AUTH_START, EXT_AUTH_ABORT, } action; - u8 bssid[ETH_ALEN]; - u8 ssid[SSID_MAX_LEN]; + const u8 *bssid; + const u8 *ssid; size_t ssid_len; unsigned int key_mgmt_suite; u16 status; + const u8 *pmkid; }; /** @@ -3377,6 +3427,14 @@ struct wpa_driver_ops { */ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); + /** + * channel_info - Get parameters of the current operating channel + * @priv: Private driver interface data + * @channel_info: Channel info structure + * Returns: 0 on success, negative (<0) on failure + */ + int (*channel_info)(void *priv, struct wpa_channel_info *channel_info); + /** * set_authmode - Set authentication algorithm(s) for static WEP * @priv: Private driver interface data @@ -3658,7 +3716,7 @@ struct wpa_driver_ops { /** * status - Get driver interface status information * @priv: Private driver interface data - * @buf: Buffer for printing tou the status information + * @buf: Buffer for printing the status information * @buflen: Maximum length of the buffer * Returns: Length of written status information or -1 on failure */ @@ -3781,6 +3839,14 @@ struct wpa_driver_ops { */ int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa); + /** + * set_receive_lowest_pn - Set receive lowest PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa); + /** * create_receive_sc - create secure channel for receiving * @priv: Private driver interface data @@ -4092,6 +4158,15 @@ struct wpa_driver_ops { */ int (*send_external_auth_status)(void *priv, struct external_auth *params); + + /** + * set_4addr_mode - Set 4-address mode + * @priv: Private driver interface data + * @bridge_ifname: Bridge interface name + * @val: 0 - disable 4addr mode, 1 - enable 4addr mode + * Returns: 0 on success, < 0 on failure + */ + int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val); }; /** @@ -5534,6 +5609,8 @@ 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 channel_width_to_int(enum chan_width width); + int ht_supported(const struct hostapd_hw_modes *mode); int vht_supported(const struct hostapd_hw_modes *mode); diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index 62f5baad6361..807cd94691d0 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -1218,8 +1218,7 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) #ifdef ATH_WPS_IE /* if WPS IE is present, preference is given to WPS */ - if (ie.wps_ie && - (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { + if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) { iebuf = ie.wps_ie; ielen = ie.wps_ie[1]; } diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 8621aa0b0099..46754968f13d 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -143,7 +143,7 @@ bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, ireq->i_data = arg; if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " + wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, " "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; } diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index ac0916e4061f..e55e6cd2b795 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -115,6 +115,25 @@ const char * channel_width_to_string(enum chan_width width) } +int channel_width_to_int(enum chan_width width) +{ + switch (width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + return 20; + case CHAN_WIDTH_40: + return 40; + case CHAN_WIDTH_80: + return 80; + case CHAN_WIDTH_80P80: + case CHAN_WIDTH_160: + return 160; + default: + return 0; + } +} + + int ht_supported(const struct hostapd_hw_modes *mode) { if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { @@ -234,7 +253,8 @@ const char * driver_flag_to_string(u64 flag) DF2S(DRIVER_IE); DF2S(SET_KEYS_AFTER_ASSOC); DF2S(DFS_OFFLOAD); - DF2S(4WAY_HANDSHAKE); + DF2S(4WAY_HANDSHAKE_PSK); + DF2S(4WAY_HANDSHAKE_8021X); DF2S(WIRED); DF2S(SME); DF2S(AP); diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 597da335e474..61b39b19721c 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -231,7 +231,11 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) } memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", + drv->iface) >= (int) sizeof(ifr.ifr_name)) { + wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); + return -1; + } if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", strerror(errno)); @@ -348,7 +352,10 @@ static int hostap_set_iface_flags(void *priv, int dev_up) struct ifreq ifr; char ifname[IFNAMSIZ]; - os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface); + if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) { + wpa_printf(MSG_ERROR, "hostap: AP interface name truncated"); + return -1; + } if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) return -1; @@ -1124,6 +1131,7 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, for (i = 0; i < 14; i++) { mode->channels[i].chan = i + 1; mode->channels[i].freq = chan2freq[i]; + mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20; /* TODO: Get allowed channel list from the driver */ if (i >= 11) mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c index 4f6629f5aaa2..9d981bb70f78 100644 --- a/src/drivers/driver_macsec_linux.c +++ b/src/drivers/driver_macsec_linux.c @@ -177,6 +177,9 @@ static int try_commit(struct macsec_drv_data *drv) if (drv->controlled_port_enabled_set) { struct rtnl_link *change = rtnl_link_alloc(); + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit controlled_port_enabled=%d", + drv->ifname, drv->controlled_port_enabled); if (!change) return -1; @@ -196,13 +199,24 @@ static int try_commit(struct macsec_drv_data *drv) drv->controlled_port_enabled_set = FALSE; } - if (drv->protect_frames_set) + if (drv->protect_frames_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit protect_frames=%d", + drv->ifname, drv->protect_frames); rtnl_link_macsec_set_protect(drv->link, drv->protect_frames); + } - if (drv->encrypt_set) + if (drv->encrypt_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d", + drv->ifname, drv->encrypt); rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt); + } if (drv->replay_protect_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit replay_protect=%d replay_window=%d", + drv->ifname, drv->replay_protect, + drv->replay_window); rtnl_link_macsec_set_replay_protect(drv->link, drv->replay_protect); if (drv->replay_protect) @@ -210,8 +224,12 @@ static int try_commit(struct macsec_drv_data *drv) drv->replay_window); } - if (drv->encoding_sa_set) + if (drv->encoding_sa_set) { + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: try_commit encoding_sa=%d", + drv->ifname, drv->encoding_sa); rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa); + } err = rtnl_link_add(drv->sk, drv->link, 0); if (err < 0) @@ -318,6 +336,8 @@ static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params) drv->common.ifname); goto cache; } + wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d", + drv->common.ifname, drv->parent_ifi); err = init_genl_ctx(drv); if (err < 0) @@ -669,6 +689,50 @@ static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa) } +/** + * macsec_drv_set_receive_lowest_pn - Set receive lowest PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, + DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d", + drv->ifname, sa->an, sa->next_pn); + + msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "failed to communicate: %d (%s)", + ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + /** * macsec_drv_get_transmit_next_pn - Get transmit next PN * @priv: Private driver interface data @@ -754,8 +818,10 @@ static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc, struct nl_msg *msg; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, - SCI2STR(sc->sci.addr, sc->sci.port)); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR + " (conf_offset=%u validation=%d)", + drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port), + conf_offset, validation); msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi); if (!msg) @@ -790,8 +856,8 @@ static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc) struct nl_msg *msg; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, - SCI2STR(sc->sci.addr, sc->sci.port)); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR, + drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port)); msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi); if (!msg) @@ -827,8 +893,17 @@ static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, - SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + wpa_printf(MSG_DEBUG, + DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR + " (enable_receive=%d next_pn=%u)", + drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port), + sa->enable_receive, sa->next_pn); + wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid", + &sa->pkey->key_identifier, + sizeof(sa->pkey->key_identifier)); + wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key", + sa->pkey->key, sa->pkey->key_len); msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi); if (!msg) @@ -877,7 +952,8 @@ static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on " + SCISTR, drv->ifname, sa->an, SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi); @@ -954,7 +1030,8 @@ static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa) struct macsec_drv_data *drv = priv; struct macsec_genl_ctx *ctx = &drv->ctx; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on " + SCISTR, drv->ifname, sa->an, SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), @@ -973,7 +1050,8 @@ static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa) struct macsec_drv_data *drv = priv; struct macsec_genl_ctx *ctx = &drv->ctx; - wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on " + SCISTR, drv->ifname, sa->an, SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), @@ -1017,7 +1095,10 @@ static int macsec_drv_create_transmit_sc( u64 sci; int err; - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, DRV_PREFIX + "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)", + drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port), + conf_offset); if (!drv->sk) { wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket"); @@ -1060,6 +1141,9 @@ static int macsec_drv_create_transmit_sc( drv->ifi = rtnl_link_get_ifindex(link); ifname = rtnl_link_get_name(link); + wpa_printf(MSG_DEBUG, + DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s", + drv->common.ifname, drv->ifi, ifname); os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); rtnl_link_put(link); @@ -1088,7 +1172,8 @@ static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc) struct macsec_drv_data *drv = priv; int err; - wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR, + drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port)); if (!drv->sk) return 0; @@ -1125,7 +1210,16 @@ static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on " + SCISTR " (enable_transmit=%d next_pn=%u)", + drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port), + sa->enable_transmit, sa->next_pn); + wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid", + &sa->pkey->key_identifier, + sizeof(sa->pkey->key_identifier)); + wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key", + sa->pkey->key, sa->pkey->key_len); msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi); if (!msg) @@ -1171,7 +1265,9 @@ static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa) struct nlattr *nest; int ret = -1; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on " + SCISTR, drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi); if (!msg) @@ -1243,7 +1339,9 @@ static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa) struct macsec_genl_ctx *ctx = &drv->ctx; int ret; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on " + SCISTR, drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE); if (ret < 0) { @@ -1269,12 +1367,38 @@ static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa) struct macsec_drv_data *drv = priv; struct macsec_genl_ctx *ctx = &drv->ctx; - wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on " + SCISTR, drv->ifname, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE); } +static int macsec_drv_status(void *priv, char *buf, size_t buflen) +{ + struct macsec_drv_data *drv = priv; + int res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, + "ifname=%s\n" + "ifi=%d\n" + "parent_ifname=%s\n" + "parent_ifi=%d\n", + drv->common.ifname, drv->ifi, + drv->ifname, drv->parent_ifi); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + return pos - buf; +} + + const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .name = "macsec_linux", .desc = "MACsec Ethernet driver for Linux", @@ -1293,6 +1417,7 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .set_current_cipher_suite = macsec_drv_set_current_cipher_suite, .enable_controlled_port = macsec_drv_enable_controlled_port, .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn, + .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn, .get_transmit_next_pn = macsec_drv_get_transmit_next_pn, .set_transmit_next_pn = macsec_drv_set_transmit_next_pn, .create_receive_sc = macsec_drv_create_receive_sc, @@ -1307,4 +1432,6 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .delete_transmit_sa = macsec_drv_delete_transmit_sa, .enable_transmit_sa = macsec_drv_enable_transmit_sa, .disable_transmit_sa = macsec_drv_disable_transmit_sa, + + .status = macsec_drv_status, }; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 871a5d0b4a20..54fe3900096a 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -40,6 +40,9 @@ #include "driver_nl80211.h" +#ifndef NETLINK_CAP_ACK +#define NETLINK_CAP_ACK 10 +#endif /* NETLINK_CAP_ACK */ /* support for extack if compilation headers are too old */ #ifndef NETLINK_EXT_ACK #define NETLINK_EXT_ACK 11 @@ -304,6 +307,7 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); drv->associated = 0; os_memset(drv->bssid, 0, ETH_ALEN); + drv->first_bss->freq = 0; } @@ -406,6 +410,11 @@ static int send_and_recv(struct nl80211_global *global, setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt)); + /* try to set NETLINK_CAP_ACK to 1, ignoring errors */ + opt = 1; + setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_CAP_ACK, &opt, sizeof(opt)); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -1539,6 +1548,70 @@ int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, } +static int get_channel_info(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1] = { 0 }; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_channel_info *chan_info = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + os_memset(chan_info, 0, sizeof(struct wpa_channel_info)); + chan_info->chanwidth = CHAN_WIDTH_UNKNOWN; + + if (tb[NL80211_ATTR_WIPHY_FREQ]) + chan_info->frequency = + nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + chan_info->chanwidth = convert2width( + nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + enum nl80211_channel_type ct = + nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + + switch (ct) { + case NL80211_CHAN_HT40MINUS: + chan_info->sec_channel = -1; + break; + case NL80211_CHAN_HT40PLUS: + chan_info->sec_channel = 1; + break; + default: + chan_info->sec_channel = 0; + break; + } + } + if (tb[NL80211_ATTR_CENTER_FREQ1]) + chan_info->center_frq1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + chan_info->center_frq2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + if (chan_info->center_frq2) { + u8 seg1_idx = 0; + + if (ieee80211_freq_to_chan(chan_info->center_frq2, &seg1_idx) != + NUM_HOSTAPD_MODES) + chan_info->seg1_idx = seg1_idx; + } + + return NL_SKIP; +} + + +static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); + return send_and_recv_msgs(drv, msg, get_channel_info, ci); +} + + static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, void *handle) { @@ -2169,6 +2242,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; #endif /* CONFIG_DPP */ #ifdef CONFIG_IEEE80211W +#ifdef CONFIG_OCV + /* SA Query Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0) + ret = -1; +#endif /* CONFIG_OCV */ /* SA Query Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) ret = -1; @@ -2392,6 +2470,16 @@ static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) if (nl80211_action_subscribe_ap(bss)) goto out_err; + if (bss->drv->device_ap_sme) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); + + /* Register for all Authentication frames */ + if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0) + < 0) + wpa_printf(MSG_DEBUG, + "nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work"); + } + nl80211_mgmt_handle_register_eloop(bss); return 0; @@ -2962,7 +3050,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, #endif /* CONFIG_DRIVER_NL80211_QCA */ if (alg == WPA_ALG_PMK && - (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) return nl80211_set_pmk(drv, key, key_len, addr); if (alg == WPA_ALG_NONE) { @@ -3400,8 +3488,8 @@ static int wpa_driver_nl80211_authenticate( goto fail; } if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -3968,7 +4056,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; u8 cmd = NL80211_CMD_NEW_BEACON; - int ret; + int ret = -ENOBUFS; int beacon_set; int num_suites; int smps_mode; @@ -3997,8 +4085,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate); wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); - wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, "nl80211: ssid=%s", + wpa_ssid_txt(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) || @@ -4083,6 +4171,11 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) goto fail; + if (drv->device_ap_sme && + (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) && + nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", params->pairwise_ciphers); num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, @@ -4174,6 +4267,29 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; } + if (params->ftm_responder) { + struct nlattr *ftm; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_FTM_RESPONDER)) { + ret = -ENOTSUP; + goto fail; + } + + ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER); + if (!ftm || + nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED) || + (params->lci && + nla_put(msg, NL80211_FTM_RESP_ATTR_LCI, + wpabuf_len(params->lci), + wpabuf_head(params->lci))) || + (params->civic && + nla_put(msg, NL80211_FTM_RESP_ATTR_CIVICLOC, + wpabuf_len(params->civic), + wpabuf_head(params->civic)))) + goto fail; + nla_nest_end(msg, ftm); + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", @@ -4225,7 +4341,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, return ret; fail: nlmsg_free(msg); - return -ENOBUFS; + return ret; } @@ -4579,7 +4695,8 @@ static int wpa_driver_nl80211_sta_add(void *priv, goto fail; #endif /* CONFIG_MESH */ - if (params->flags & WPA_STA_WMM) { + if ((!params->set || FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) && + (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); @@ -5189,8 +5306,8 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) goto fail; - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(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); @@ -5351,8 +5468,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, } if (params->ssid) { - wpa_hexdump_ascii(MSG_DEBUG, " * SSID", - params->ssid, params->ssid_len); + wpa_printf(MSG_DEBUG, " * SSID=%s", + wpa_ssid_txt(params->ssid, params->ssid_len)); if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) return -1; @@ -5410,8 +5527,11 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, 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_SAE || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 || params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 || @@ -5442,12 +5562,21 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_OSEN: mgmt = RSN_AUTH_KEY_MGMT_OSEN; break; + case WPA_KEY_MGMT_SAE: + mgmt = RSN_AUTH_KEY_MGMT_SAE; + break; + case WPA_KEY_MGMT_FT_SAE: + mgmt = RSN_AUTH_KEY_MGMT_FT_SAE; + break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; break; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; + break; case WPA_KEY_MGMT_FILS_SHA256: mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; break; @@ -5476,9 +5605,16 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } + if (params->req_key_mgmt_offload && + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) { + wpa_printf(MSG_DEBUG, " * WANT_1X_4WAY_HS"); + if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS)) + return -1; + } + /* Add PSK in case of 4-way handshake offload */ if (params->psk && - (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) { + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) { wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32); if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk)) return -1; @@ -5609,9 +5745,10 @@ static int wpa_driver_nl80211_try_connect( goto fail; if (nl_connect) - ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + ret = send_and_recv(drv->global, nl_connect, msg, + NULL, (void *) -1); else - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); msg = NULL; if (ret) { @@ -5623,6 +5760,7 @@ static int wpa_driver_nl80211_try_connect( } fail: + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return ret; @@ -6023,6 +6161,7 @@ static int get_key_handler(struct nl_msg *msg, void *arg) 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)); + nl80211_nlmsg_clear(msg); return NL_SKIP; } @@ -6057,7 +6196,7 @@ static int i802_set_rts(void *priv, int rts) int ret; u32 val; - if (rts >= 2347) + if (rts >= 2347 || rts == -1) val = (u32) -1; else val = rts; @@ -6085,7 +6224,7 @@ static int i802_set_frag(void *priv, int frag) int ret; u32 val; - if (frag >= 2346) + if (frag >= 2346 || frag == -1) val = (u32) -1; else val = frag; @@ -6646,9 +6785,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, } return i802_set_sta_vlan(priv, addr, name, 0); } else { - if (bridge_ifname) - linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, - name); + if (bridge_ifname && + linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, + name) < 0) + wpa_printf(MSG_INFO, + "nl80211: Failed to remove interface %s from bridge %s: %s", + name, bridge_ifname, strerror(errno)); i802_set_sta_vlan(priv, addr, bss->ifname, 0); nl80211_remove_iface(drv, if_nametoindex(name)); @@ -7815,13 +7957,15 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, (params->fils_cache_id && nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2, params->fils_cache_id)) || - (params->pmk_len && params->pmk_len <= PMK_MAX_LEN && + (cmd != NL80211_CMD_DEL_PMKSA && + params->pmk_len && params->pmk_len <= PMK_MAX_LEN && nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) { + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return -ENOBUFS; } - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1); } @@ -8144,6 +8288,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + u64 cookie; int ret; if (!drv->poll_command_supported) { @@ -8157,11 +8302,16 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, return; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); if (ret < 0) { wpa_printf(MSG_DEBUG, "nl80211: Client probe request for " MACSTR " failed: ret=%d (%s)", MAC2STR(addr), ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Client probe request addr=" MACSTR + " cookie=%llu", MAC2STR(addr), + (long long unsigned int) cookie); } } @@ -8593,6 +8743,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) struct wpa_driver_nl80211_data *drv = bss->drv; int res; char *pos, *end; + struct nl_msg *msg; + char alpha2[3] = { 0, 0, 0 }; pos = buf; end = buf + buflen; @@ -8737,6 +8889,23 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) pos += res; } + msg = nlmsg_alloc(); + if (msg && + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) && + nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) { + if (send_and_recv_msgs(drv, msg, nl80211_get_country, + alpha2) == 0 && + alpha2[0]) { + res = os_snprintf(pos, end - pos, "country=%s\n", + alpha2); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + } + } else { + nlmsg_free(msg); + } + return pos - buf; } @@ -9303,8 +9472,8 @@ 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); + wpa_printf(MSG_DEBUG, " * Mesh ID (SSID)=%s", + wpa_ssid_txt(mesh_id, mesh_id_len)); return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id); } @@ -10645,15 +10814,27 @@ static int nl80211_send_external_auth_status(void *priv, struct nl_msg *msg = NULL; int ret = -1; + /* External auth command/status is intended for drivers that implement + * intenral SME but want to offload authentication processing (e.g., + * SAE) to hostapd/wpa_supplicant. Do nott send the status to drivers + * which do not support AP SME or use wpa_supplicant/hostapd SME. + */ + if (!bss->drv->device_ap_sme || + (drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return -1; + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: External auth status: %u", params->status); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH); if (!msg || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) || - nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid) || - nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)) + (params->ssid && params->ssid_len && + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) || + (params->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) || + (params->bssid && + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))) goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -10669,6 +10850,49 @@ static int nl80211_send_external_auth_status(void *priv, } +static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname, + int val) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)", + val ? "Enable" : "Disable", bridge_ifname); + + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE); + if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val)) + goto fail; + + if (bridge_ifname[0] && bss->added_if_into_bridge && !val) { + if (linux_br_del_if(drv->global->ioctl_sock, + bridge_ifname, bss->ifname)) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to remove interface %s from bridge %s", + bss->ifname, bridge_ifname); + return -1; + } + bss->added_if_into_bridge = 0; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (!ret) { + if (bridge_ifname[0] && val && + i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0) + return -1; + return 0; + } + +fail: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr"); + + return ret; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -10728,6 +10952,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .resume = wpa_driver_nl80211_resume, .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, + .channel_info = nl80211_channel_info, .send_frame = nl80211_send_frame, .set_param = nl80211_set_param, .get_radio_name = nl80211_get_radio_name, @@ -10797,4 +11022,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ext_capab = nl80211_get_ext_capab, .update_connect_params = nl80211_update_connection_params, .send_external_auth_status = nl80211_send_external_auth_status, + .set_4addr_mode = nl80211_set_4addr_mode, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index bc562ba7a8cc..1e7fe7a98fff 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -166,6 +166,7 @@ struct wpa_driver_nl80211_data { unsigned int he_capab_vendor_cmd_avail:1; unsigned int fetch_bss_trans_status:1; unsigned int roam_vendor_cmd_avail:1; + unsigned int get_supported_akm_suites_avail:1; u64 vendor_scan_cookie; u64 remain_on_chan_cookie; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 7b360d209aba..37eeb5e6686d 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -403,10 +403,11 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD; if (ext_feature_isset(ext_features, len, - NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) && - ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK)) + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK; + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) - capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_MFP_OPTIONAL)) @@ -428,6 +429,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION)) capa->flags |= WPA_DRIVER_FLAGS_OCE_STA; #endif /* CONFIG_MBO */ + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) + capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER; } @@ -782,6 +787,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_ROAM: drv->roam_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: + drv->get_supported_akm_suites_avail = 1; + break; #endif /* CONFIG_DRIVER_NL80211_QCA */ } } @@ -954,6 +962,126 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) } +static unsigned int get_akm_suites_info(struct nlattr *tb) +{ + int i, num; + unsigned int key_mgmt = 0; + u32 *akms; + + if (!tb) + return 0; + + num = nla_len(tb) / sizeof(u32); + akms = nla_data(tb); + for (i = 0; i < num; i++) { + u32 a = akms[i]; + + wpa_printf(MSG_DEBUG, + "nl80211: Supported AKM %02x-%02x-%02x:%u", + a >> 24, (a >> 16) & 0xff, + (a >> 8) & 0xff, a & 0xff); + switch (a) { + case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2; + break; + case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + break; + case RSN_AUTH_KEY_MGMT_FT_802_1X: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT; + break; + case RSN_AUTH_KEY_MGMT_FT_PSK: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + break; + case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B; + break; + case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; + break; + case RSN_AUTH_KEY_MGMT_OWE: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE; + break; + case RSN_AUTH_KEY_MGMT_DPP: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP; + break; + case RSN_AUTH_KEY_MGMT_FILS_SHA256: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256; + break; + case RSN_AUTH_KEY_MGMT_FILS_SHA384: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; + break; + case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256; + break; + case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; + break; + case RSN_AUTH_KEY_MGMT_SAE: + key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE; + break; + } + } + + return key_mgmt; +} + + +static int get_akm_suites_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + unsigned int *key_mgmt = 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_data[NL80211_ATTR_MAX + 1]; + + nla_parse(tb_data, NL80211_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + *key_mgmt = + get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]); + } + + return NL_SKIP; +} + + +static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + unsigned int key_mgmt = 0; + int ret; + + if (!drv->get_supported_akm_suites_avail) + 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_GET_SUPPORTED_AKMS)) { + nlmsg_free(msg); + return -1; + } + + ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt); + if (!ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x", + key_mgmt); + drv->capa.key_mgmt = key_mgmt; + } + + return ret; +} + + static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -1179,11 +1307,18 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 | WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 | - WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; + WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 | + WPA_DRIVER_CAPA_KEY_MGMT_SAE; else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; +#ifdef CONFIG_DRIVER_NL80211_QCA + /* Override drv->capa.key_mgmt based on driver advertised capability + * constraints, if available. */ + qca_nl80211_get_akm_suites(drv); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -1307,6 +1442,7 @@ static void phy_info_freq(struct hostapd_hw_modes *mode, u8 channel; chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); chan->flag = 0; + chan->allowed_bw = ~0; chan->dfs_cac_ms = 0; if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) chan->chan = channel; @@ -1322,6 +1458,19 @@ static void phy_info_freq(struct hostapd_hw_modes *mode, if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT]) chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ]) + chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160; + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); @@ -1356,6 +1505,12 @@ static int phy_info_freqs(struct phy_info_arg *phy_info, [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG }, }; int new_channels = 0; struct hostapd_channel_data *channel; @@ -1932,6 +2087,61 @@ static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, } +static const char * modestr(enum hostapd_hw_mode mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211B: + return "802.11b"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; + default: + return "?"; + } +} + + +static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes, + u16 num_modes) +{ + int i; + + if (!modes) + return; + + for (i = 0; i < num_modes; i++) { + struct hostapd_hw_modes *mode = &modes[i]; + char str[200]; + char *pos = str; + char *end = pos + sizeof(str); + int j, res; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan = &mode->channels[j]; + + res = os_snprintf(pos, end - pos, " %d%s%s%s", + chan->freq, + (chan->flag & HOSTAPD_CHAN_DISABLED) ? + "[DISABLED]" : "", + (chan->flag & HOSTAPD_CHAN_NO_IR) ? + "[NO_IR]" : "", + (chan->flag & HOSTAPD_CHAN_RADAR) ? + "[RADAR]" : ""); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + + *pos = '\0'; + wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s", + modestr(mode->mode), str); + } +} + + struct hostapd_hw_modes * nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, u8 *dfs_domain) @@ -1963,6 +2173,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, } if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + struct hostapd_hw_modes *modes; + nl80211_set_regulatory_flags(drv, &result); if (result.failed) { int i; @@ -1978,8 +2190,10 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, *dfs_domain = result.dfs_domain; - return wpa_driver_nl80211_postprocess_modes(result.modes, - num_modes); + modes = wpa_driver_nl80211_postprocess_modes(result.modes, + num_modes); + nl80211_dump_chan_list(modes, *num_modes); + return modes; } return NULL; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 205b4cd4b082..ee7b4da38d87 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -206,7 +206,8 @@ static void nl80211_parse_wmm_params(struct nlattr *wmm_attr, static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len, struct nlattr *wmm) + const u8 *frame, size_t len, struct nlattr *wmm, + struct nlattr *req_ie) { const struct ieee80211_mgmt *mgmt; union wpa_event_data event; @@ -261,7 +262,10 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, len - 24 - sizeof(mgmt->u.assoc_resp); } - event.assoc_info.freq = drv->assoc_freq; + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } /* When this association was initiated outside of wpa_supplicant, * drv->ssid needs to be set here to satisfy later checking. */ @@ -273,6 +277,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, wpa_ssid_txt(drv->ssid, drv->ssid_len)); } + event.assoc_info.freq = drv->assoc_freq; + drv->first_bss->freq = drv->assoc_freq; + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); @@ -402,6 +409,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, } event.assoc_info.freq = nl80211_get_assoc_freq(drv); + drv->first_bss->freq = drv->assoc_freq; if ((!ssid || ssid[1] == 0 || ssid[1] > 32) && (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) { @@ -868,7 +876,7 @@ static void mlme_event(struct i802_bss *bss, struct nlattr *addr, struct nlattr *timed_out, struct nlattr *freq, struct nlattr *ack, struct nlattr *cookie, struct nlattr *sig, - struct nlattr *wmm) + struct nlattr *wmm, struct nlattr *req_ie) { struct wpa_driver_nl80211_data *drv = bss->drv; const u8 *data; @@ -917,7 +925,8 @@ static void mlme_event(struct i802_bss *bss, 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); + mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm, + req_ie); break; case NL80211_CMD_DEAUTHENTICATE: mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, @@ -1429,16 +1438,23 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { union wpa_event_data data; + const u8 *addr; + u64 cookie = 0; - wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); - - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + addr = nla_data(tb[NL80211_ATTR_MAC]); + if (!addr) + return; + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR + " ack=%d cookie=%llu)", MAC2STR(addr), + tb[NL80211_ATTR_ACK] != NULL, + (long long unsigned int) cookie); + if (!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); - + os_memcpy(data.client_poll.addr, addr, ETH_ALEN); wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); } @@ -2183,6 +2199,54 @@ static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, } +static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq) +{ + 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 }, + }; + struct nlattr *tb[NL80211_FREQUENCY_ATTR_MAX + 1]; + u32 freq = 0, max_tx_power = 0; + + nla_parse(tb, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + + if (tb[NL80211_FREQUENCY_ATTR_FREQ]) + freq = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]); + if (tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) + max_tx_power = + nla_get_u32(tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]); + + wpa_printf(MSG_DEBUG, + "nl80211: Channel (%s): freq=%u max_tx_power=%u%s%s%s", + title, freq, max_tx_power, + tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "", + tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "", + tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : ""); +} + + +static void nl80211_reg_beacon_hint_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + os_memset(&data, 0, sizeof(data)); + data.channel_list_changed.initiator = REGDOM_BEACON_HINT; + + if (tb[NL80211_ATTR_FREQ_BEFORE]) + nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]); + if (tb[NL80211_ATTR_FREQ_AFTER]) + nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]); + + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); +} + + static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, struct nlattr **tb) { @@ -2214,11 +2278,9 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]); if (event.external_auth.ssid_len > SSID_MAX_LEN) return; - os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]), - event.external_auth.ssid_len); + event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]); - os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]), - ETH_ALEN); + event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]); wpa_printf(MSG_DEBUG, "nl80211: External auth action: %u, AKM: 0x%x", @@ -2327,7 +2389,6 @@ 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; int external_scan_event = 0; wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", @@ -2428,7 +2489,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], - tb[NL80211_ATTR_STA_WME]); + tb[NL80211_ATTR_STA_WME], + tb[NL80211_ATTR_REQ_IE]); break; case NL80211_CMD_CONNECT: case NL80211_CMD_ROAM: @@ -2480,11 +2542,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, 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); + nl80211_reg_beacon_hint_event(drv, tb); break; case NL80211_CMD_NEW_STATION: nl80211_new_station_event(drv, bss, tb); @@ -2605,7 +2663,7 @@ int process_bss_event(struct nl_msg *msg, void *arg) tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], tb[NL80211_ATTR_COOKIE], tb[NL80211_ATTR_RX_SIGNAL_DBM], - tb[NL80211_ATTR_STA_WME]); + tb[NL80211_ATTR_STA_WME], NULL); break; case NL80211_CMD_UNEXPECTED_FRAME: nl80211_spurious_frame(bss, tb, 0); diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 33a8d359848f..9afa5b304cf8 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -197,9 +197,9 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, 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); + wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s", + wpa_ssid_txt(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; @@ -537,10 +537,10 @@ int wpa_driver_nl80211_sched_scan(void *priv, 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); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID %s", + wpa_ssid_txt(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 || @@ -1098,9 +1098,9 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, 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); + wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s", + wpa_ssid_txt(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; diff --git a/src/drivers/driver_openbsd.c b/src/drivers/driver_openbsd.c index e94eda08f621..c06e75c0f284 100644 --- a/src/drivers/driver_openbsd.c +++ b/src/drivers/driver_openbsd.c @@ -62,7 +62,8 @@ 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; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK | + WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; return 0; } diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index e8a51354dd0c..9beb6c46d367 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -290,21 +290,26 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, wpa_driver_roboswitch_addr_be16(addr, addr_be16); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, - &_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, 1) < 0) + return -1; /* If ARL control is disabled, there is nothing to leave. */ if (!(_read & (1 << 4))) return -1; - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, - &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0) + return -1; /* check if we occupy multiport address 1 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, + 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, + 1) < 0) + return -1; /* and multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { @@ -327,10 +332,13 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, &ports_read, 1); } } else { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_read, 3); - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &ports_read, 1); + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, + 3) < 0 || + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, + 1) < 0) + return -1; /* or multiport address 2 */ if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 20abaab4cd84..f7755cccde27 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -868,14 +868,16 @@ static int wext_hostap_ifname(struct wpa_driver_wext_data *drv, const char *ifname) { char buf[200], *res; - int type; + int type, ret; FILE *f; if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0) return -1; - snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type", - drv->ifname, ifname); + ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type", + drv->ifname, ifname); + if (os_snprintf_error(sizeof(buf), ret)) + return -1; f = fopen(buf, "r"); if (!f) @@ -1645,7 +1647,8 @@ static int wpa_driver_wext_get_range(void *priv) if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) - drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK | + WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X; drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -1676,7 +1679,7 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) return 0; if (!psk) diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index 1496b47d0ad5..442c59cf4a5f 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -22,6 +22,7 @@ ifdef CONFIG_DRIVER_MACSEC_LINUX DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX DRV_OBJS += ../src/drivers/driver_macsec_linux.o NEED_DRV_WIRED_COMMON=1 +NEED_LIBNL=y CONFIG_LIBNL3_ROUTE=y endif @@ -51,37 +52,7 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y NEED_RADIOTAP=y - -ifdef CONFIG_LIBNL32 - DRV_LIBS += -lnl-3 - DRV_LIBS += -lnl-genl-3 - 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 - else - ifndef CONFIG_OSX - DRV_LIBS += -lnl - endif - endif - - ifdef CONFIG_LIBNL20 - ifndef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-genl - endif - DRV_CFLAGS += -DCONFIG_LIBNL20 - endif -endif +NEED_LIBNL=y endif ifdef CONFIG_DRIVER_BSD @@ -183,26 +154,55 @@ endif ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN +NEED_LIBNL=y +CONFIG_LIBNL3_ROUTE=y +endif +endif + +ifdef NEED_LIBNL +ifndef CONFIG_LIBNL32 +ifndef CONFIG_LIBNL20 +ifndef CONFIG_LIBNL_TINY +PKG_CONFIG ?= pkg-config +HAVE_LIBNL3 := $(shell $(PKG_CONFIG) --exists libnl-3.0; echo $$?) +ifeq ($(HAVE_LIBNL3),0) +CONFIG_LIBNL32=y +endif +endif +endif +endif + ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 DRV_LIBS += -lnl-genl-3 - DRV_LIBS += -lnl-route-3 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 else - DRV_LIBS += -lnl + ifndef CONFIG_OSX + DRV_LIBS += -lnl + endif endif ifdef CONFIG_LIBNL20 - DRV_LIBS += -lnl-genl - DRV_LIBS += -lnl-route + ifndef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-genl + endif DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif -endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index cd25133af808..599a0b5794fe 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -23,6 +23,7 @@ DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX DRV_OBJS += src/drivers/driver_macsec_linux.c NEED_DRV_WIRED_COMMON=1 CONFIG_LIBNL3_ROUTE=y +NEED_LIBNL=y endif ifdef NEED_DRV_WIRED_COMMON @@ -46,29 +47,7 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y NEED_RADIOTAP=y - -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 - else - DRV_LIBS += -lnl - endif - - ifdef CONFIG_LIBNL20 - ifndef CONFIG_LIBNL_TINY - DRV_LIBS += -lnl-genl - endif - DRV_CFLAGS += -DCONFIG_LIBNL20 - endif -endif +NEED_LIBNL=y endif ifdef CONFIG_DRIVER_BSD @@ -171,11 +150,20 @@ endif ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN +NEED_LIBNL=y +CONFIG_LIBNL3_ROUTE=y +endif +endif + +ifdef NEED_LIBNL 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_LIBNL20 + DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE +endif else ifdef CONFIG_LIBNL_TINY DRV_LIBS += -lnl-tiny @@ -184,13 +172,13 @@ else endif ifdef CONFIG_LIBNL20 - DRV_LIBS += -lnl-genl - DRV_LIBS += -lnl-route + ifndef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-genl + endif DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif endif -endif ##### COMMON VARS DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c index e21147af1bcd..7edb9df2edd4 100644 --- a/src/drivers/linux_ioctl.c +++ b/src/drivers/linux_ioctl.c @@ -12,6 +12,7 @@ #include #include "utils/common.h" +#include "common/linux_bridge.h" #include "linux_ioctl.h" @@ -119,25 +120,14 @@ int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr) } -#ifndef SIOCBRADDBR -#define SIOCBRADDBR 0x89a0 -#endif -#ifndef SIOCBRDELBR -#define SIOCBRDELBR 0x89a1 -#endif -#ifndef SIOCBRADDIF -#define SIOCBRADDIF 0x89a2 -#endif -#ifndef SIOCBRDELIF -#define SIOCBRDELIF 0x89a3 -#endif - - int linux_br_add(int sock, const char *brname) { if (ioctl(sock, SIOCBRADDBR, brname) < 0) { + int saved_errno = errno; + wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s", brname, strerror(errno)); + errno = saved_errno; return -1; } @@ -170,8 +160,11 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname) os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); ifr.ifr_ifindex = ifindex; if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) { + int saved_errno = errno; + wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge " "%s: %s", ifname, brname, strerror(errno)); + errno = saved_errno; return -1; } diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 1766a12b231c..dd4f86ee286e 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -1033,6 +1033,38 @@ * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its * address(specified in %NL80211_ATTR_MAC). * + * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in + * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute. + * + * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s) + * with the given parameters, which are encapsulated in the nested + * %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address + * randomization may be enabled and configured by specifying the + * %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes. + * If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute. + * A u64 cookie for further %NL80211_ATTR_COOKIE use is is returned in + * the netlink extended ack message. + * + * To cancel a measurement, close the socket that requested it. + * + * Measurement results are reported to the socket that requested the + * measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they + * become available, so applications must ensure a large enough socket + * buffer size. + * + * Depending on driver support it may or may not be possible to start + * multiple concurrent measurements. + * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the + * result notification from the driver to the requesting socket. + * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that + * the measurement completed, using the measurement cookie + * (%NL80211_ATTR_COOKIE). + * + * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was + * detected and reported by a neighboring device on the channel + * indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes + * determining the width and type. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1245,6 +1277,14 @@ enum nl80211_commands { NL80211_CMD_CONTROL_PORT_FRAME, + NL80211_CMD_GET_FTM_RESPONDER_STATS, + + NL80211_CMD_PEER_MEASUREMENT_START, + NL80211_CMD_PEER_MEASUREMENT_RESULT, + NL80211_CMD_PEER_MEASUREMENT_COMPLETE, + + NL80211_CMD_NOTIFY_RADAR, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1525,6 +1565,12 @@ enum nl80211_commands { * (a u32 with flags from &enum nl80211_wpa_versions). * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to * indicate which key management algorithm(s) to use (an array of u32). + * This attribute is also sent in response to @NL80211_CMD_GET_WIPHY, + * indicating the supported AKM suites, intended for specific drivers which + * implement SME and have constraints on which AKMs are supported and also + * the cases where an AKM support is offloaded to the driver/firmware. + * If there is no such notification from the driver, user space should + * assume the driver supports all the AKM suites. * * @NL80211_ATTR_REQ_IE: (Re)association request information elements as * sent out by the card, for ROAM and successful CONNECT events. @@ -1701,7 +1747,7 @@ enum nl80211_commands { * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID * is included in the probe request, but the match attributes * will never let it go through), -EINVAL may be returned. - * If ommited, no filtering is done. + * If omitted, no filtering is done. * * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported * interface combinations. In each nested item, it contains attributes @@ -1806,7 +1852,7 @@ enum nl80211_commands { * * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be * used by the drivers which has MLME in firmware and does not have support - * to report per station tx/rx activity to free up the staion entry from + * to report per station tx/rx activity to free up the station entry from * the list. This needs to be used when the driver advertises the * capability to timeout the stations. * @@ -2167,7 +2213,7 @@ enum nl80211_commands { * * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in * the specified band is to be adjusted before doing - * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out + * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out * better BSSs. The attribute value is a packed structure * value as specified by &struct nl80211_bss_select_rssi_adjust. * @@ -2220,10 +2266,10 @@ enum nl80211_commands { * &enum nl80211_external_auth_action value). This is used with the * %NL80211_CMD_EXTERNAL_AUTH request event. * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user - * space supports external authentication. This attribute shall be used - * only with %NL80211_CMD_CONNECT request. The driver may offload - * authentication processing to user space if this capability is indicated - * in NL80211_CMD_CONNECT requests from the user space. + * space supports external authentication. This attribute shall be used + * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver + * may offload authentication processing to user space if this capability + * is indicated in the respective requests from the user space. * * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. @@ -2241,6 +2287,27 @@ enum nl80211_commands { * association request when used with NL80211_CMD_NEW_STATION). Can be set * only if %NL80211_STA_FLAG_WME is set. * + * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include + * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing + * measurement (FTM) responder functionality and containing parameters as + * possible, see &enum nl80211_ftm_responder_attr + * + * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder + * statistics, see &enum nl80211_ftm_responder_stats. + * + * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32), + * if the attribute is not given no timeout is requested. Note that 0 is an + * invalid value. + * + * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result) + * data, uses nested attributes specified in + * &enum nl80211_peer_measurement_attrs. + * This is also used for capability advertisement in the wiphy information, + * with the appropriate sub-attributes. + * + * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime + * scheduler. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2682,6 +2749,16 @@ enum nl80211_attrs { NL80211_ATTR_HE_CAPABILITY, + NL80211_ATTR_FTM_RESPONDER, + + NL80211_ATTR_FTM_RESPONDER_STATS, + + NL80211_ATTR_TIMEOUT, + + NL80211_ATTR_PEER_MEASUREMENTS, + + NL80211_ATTR_AIRTIME_WEIGHT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3051,6 +3128,17 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm) + * @NL80211_STA_INFO_RX_MPDUS: total number of received packets (MPDUs) + * (u32, from this station) + * @NL80211_STA_INFO_FCS_ERROR_COUNT: total number of packets (MPDUs) received + * with an FCS error (u32, from this station). This count may not include + * some packets with an FCS error due to TA corruption. Hence this counter + * might not be fully accurate. + * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a + * mesh gate (u8, 0 or 1) + * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames + * sent to the station (u64, usec) + * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -3091,6 +3179,11 @@ enum nl80211_sta_info { NL80211_STA_INFO_PAD, NL80211_STA_INFO_ACK_SIGNAL, NL80211_STA_INFO_ACK_SIGNAL_AVG, + NL80211_STA_INFO_RX_MPDUS, + NL80211_STA_INFO_FCS_ERROR_COUNT, + NL80211_STA_INFO_CONNECTED_TO_GATE, + NL80211_STA_INFO_TX_DURATION, + NL80211_STA_INFO_AIRTIME_WEIGHT, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -3200,8 +3293,10 @@ enum nl80211_mpath_flags { * &enum nl80211_mpath_flags; * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination + * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number - * currently defind + * currently defined * @__NL80211_MPATH_INFO_AFTER_LAST: internal use */ enum nl80211_mpath_info { @@ -3213,6 +3308,8 @@ enum nl80211_mpath_info { NL80211_MPATH_INFO_FLAGS, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, NL80211_MPATH_INFO_DISCOVERY_RETRIES, + NL80211_MPATH_INFO_HOP_COUNT, + NL80211_MPATH_INFO_PATH_CHANGE, /* keep last */ __NL80211_MPATH_INFO_AFTER_LAST, @@ -3870,6 +3967,11 @@ enum nl80211_mesh_power_mode { * remove it from the STA's list of peers. You may set this to 0 to disable * the removal of the STA. Default is 30 minutes. * + * @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA + * will advertise that it is connected to a gate in the mesh formation + * field. If left unset then the mesh formation field will only + * advertise such if there is an active root mesh path. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -3902,6 +4004,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_POWER_MODE, NL80211_MESHCONF_AWAKE_WINDOW, NL80211_MESHCONF_PLINK_TIMEOUT, + NL80211_MESHCONF_CONNECTED_TO_GATE, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, @@ -4834,7 +4937,7 @@ enum nl80211_iface_limit_attrs { * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 * => allows a STA plus three P2P interfaces * - * The list of these four possiblities could completely be contained + * The list of these four possibilities could completely be contained * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate * that any of these groups must match. * @@ -4864,7 +4967,7 @@ enum nl80211_if_combination_attrs { * enum nl80211_plink_state - state of a mesh peer link finite state machine * * @NL80211_PLINK_LISTEN: initial state, considered the implicit - * state of non existant mesh peer links + * state of non existent mesh peer links * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to * this mesh peer * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received @@ -5225,6 +5328,20 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data * except for supported rates from the probe request content if requested * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag. + * @NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER: Driver supports enabling fine + * timing measurement responder role. + * + * @NL80211_EXT_FEATURE_CAN_REPLACE_PTK0: Driver/device confirm that they are + * able to rekey an in-use key correctly. Userspace must not rekey PTK keys + * if this flag is not set. Ignoring this can leak clear text packets and/or + * freeze the connection. + * + * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime + * fairness for transmitted packets and has enabled airtime fairness + * scheduling. + * + * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching + * (set/del PMKSA operations) in AP mode. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -5263,6 +5380,10 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_TXQS, NL80211_EXT_FEATURE_SCAN_RANDOM_SN, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, + NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -5347,7 +5468,7 @@ enum nl80211_timeout_reason { * request parameters IE in the probe request * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at - * rate of at least 5.5M. In case non OCE AP is dicovered in the channel, + * rate of at least 5.5M. In case non OCE AP is discovered in the channel, * only the first probe req in the channel will be sent in high rate. * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request * tx deferral (dot11FILSProbeDelay shall be set to 15ms) @@ -5514,9 +5635,14 @@ enum nl80211_crit_proto_id { * Used by cfg80211_rx_mgmt() * * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload + * the authentication. Exclusively defined for host drivers that + * advertises the SME functionality but would like the userspace + * to handle certain authentication algorithms (e.g. SAE). */ enum nl80211_rxmgmt_flags { NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1, }; /* @@ -5802,4 +5928,458 @@ enum nl80211_external_auth_action { NL80211_EXTERNAL_AUTH_ABORT, }; +/** + * enum nl80211_ftm_responder_attributes - fine timing measurement + * responder attributes + * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid + * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled + * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element + * (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10), + * i.e. starting with the measurement token + * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element + * (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13), + * i.e. starting with the measurement token + * @__NL80211_FTM_RESP_ATTR_LAST: Internal + * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute. + */ +enum nl80211_ftm_responder_attributes { + __NL80211_FTM_RESP_ATTR_INVALID, + + NL80211_FTM_RESP_ATTR_ENABLED, + NL80211_FTM_RESP_ATTR_LCI, + NL80211_FTM_RESP_ATTR_CIVICLOC, + + /* keep last */ + __NL80211_FTM_RESP_ATTR_LAST, + NL80211_FTM_RESP_ATTR_MAX = __NL80211_FTM_RESP_ATTR_LAST - 1, +}; + +/* + * enum nl80211_ftm_responder_stats - FTM responder statistics + * + * These attribute types are used with %NL80211_ATTR_FTM_RESPONDER_STATS + * when getting FTM responder statistics. + * + * @__NL80211_FTM_STATS_INVALID: attribute number 0 is reserved + * @NL80211_FTM_STATS_SUCCESS_NUM: number of FTM sessions in which all frames + * were ssfully answered (u32) + * @NL80211_FTM_STATS_PARTIAL_NUM: number of FTM sessions in which part of the + * frames were successfully answered (u32) + * @NL80211_FTM_STATS_FAILED_NUM: number of failed FTM sessions (u32) + * @NL80211_FTM_STATS_ASAP_NUM: number of ASAP sessions (u32) + * @NL80211_FTM_STATS_NON_ASAP_NUM: number of non-ASAP sessions (u32) + * @NL80211_FTM_STATS_TOTAL_DURATION_MSEC: total sessions durations - gives an + * indication of how much time the responder was busy (u64, msec) + * @NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM: number of unknown FTM triggers - + * triggers from initiators that didn't finish successfully the negotiation + * phase with the responder (u32) + * @NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM: number of FTM reschedule requests + * - initiator asks for a new scheduling although it already has scheduled + * FTM slot (u32) + * @NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM: number of FTM triggers out of + * scheduled window (u32) + * @NL80211_FTM_STATS_PAD: used for padding, ignore + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_FTM_STATS_MAX: highest possible FTM responder stats attribute + */ +enum nl80211_ftm_responder_stats { + __NL80211_FTM_STATS_INVALID, + NL80211_FTM_STATS_SUCCESS_NUM, + NL80211_FTM_STATS_PARTIAL_NUM, + NL80211_FTM_STATS_FAILED_NUM, + NL80211_FTM_STATS_ASAP_NUM, + NL80211_FTM_STATS_NON_ASAP_NUM, + NL80211_FTM_STATS_TOTAL_DURATION_MSEC, + NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM, + NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM, + NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM, + NL80211_FTM_STATS_PAD, + + /* keep last */ + __NL80211_FTM_STATS_AFTER_LAST, + NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_preamble - frame preamble types + * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble + * @NL80211_PREAMBLE_HT: HT preamble + * @NL80211_PREAMBLE_VHT: VHT preamble + * @NL80211_PREAMBLE_DMG: DMG preamble + */ +enum nl80211_preamble { + NL80211_PREAMBLE_LEGACY, + NL80211_PREAMBLE_HT, + NL80211_PREAMBLE_VHT, + NL80211_PREAMBLE_DMG, +}; + +/** + * enum nl80211_peer_measurement_type - peer measurement types + * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use + * these numbers also for attributes + * + * @NL80211_PMSR_TYPE_FTM: flight time measurement + * + * @NUM_NL80211_PMSR_TYPES: internal + * @NL80211_PMSR_TYPE_MAX: highest type number + */ +enum nl80211_peer_measurement_type { + NL80211_PMSR_TYPE_INVALID, + + NL80211_PMSR_TYPE_FTM, + + NUM_NL80211_PMSR_TYPES, + NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1 +}; + +/** + * enum nl80211_peer_measurement_status - peer measurement status + * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully + * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused + * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out + * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent + * reason may be available in the response data + */ +enum nl80211_peer_measurement_status { + NL80211_PMSR_STATUS_SUCCESS, + NL80211_PMSR_STATUS_REFUSED, + NL80211_PMSR_STATUS_TIMEOUT, + NL80211_PMSR_STATUS_FAILURE, +}; + +/** + * enum nl80211_peer_measurement_req - peer measurement request attributes + * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid + * + * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement + * type-specific request data inside. The attributes used are from the + * enums named nl80211_peer_measurement__req. + * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported + * (flag attribute) + * + * @NUM_NL80211_PMSR_REQ_ATTRS: internal + * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_req { + __NL80211_PMSR_REQ_ATTR_INVALID, + + NL80211_PMSR_REQ_ATTR_DATA, + NL80211_PMSR_REQ_ATTR_GET_AP_TSF, + + /* keep last */ + NUM_NL80211_PMSR_REQ_ATTRS, + NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1 +}; + +/** + * enum nl80211_peer_measurement_resp - peer measurement response attributes + * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid + * + * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement + * type-specific results inside. The attributes used are from the enums + * named nl80211_peer_measurement__resp. + * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status + * (using values from &enum nl80211_peer_measurement_status.) + * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the + * result was measured; this value is not expected to be accurate to + * more than 20ms. (u64, nanoseconds) + * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface + * doing the measurement is connected to when the result was measured. + * This shall be accurately reported if supported and requested + * (u64, usec) + * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially + * (*e.g. with FTM per-burst data) this flag will be cleared on all but + * the last result; if all results are combined it's set on the single + * result. + * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore + * + * @NUM_NL80211_PMSR_RESP_ATTRS: internal + * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_resp { + __NL80211_PMSR_RESP_ATTR_INVALID, + + NL80211_PMSR_RESP_ATTR_DATA, + NL80211_PMSR_RESP_ATTR_STATUS, + NL80211_PMSR_RESP_ATTR_HOST_TIME, + NL80211_PMSR_RESP_ATTR_AP_TSF, + NL80211_PMSR_RESP_ATTR_FINAL, + NL80211_PMSR_RESP_ATTR_PAD, + + /* keep last */ + NUM_NL80211_PMSR_RESP_ATTRS, + NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1 +}; + +/** + * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement + * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid + * + * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address + * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level + * attributes like %NL80211_ATTR_WIPHY_FREQ etc. + * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by + * measurement type, with attributes from the + * &enum nl80211_peer_measurement_req inside. + * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by + * measurement type, with attributes from the + * &enum nl80211_peer_measurement_resp inside. + * + * @NUM_NL80211_PMSR_PEER_ATTRS: internal + * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_peer_attrs { + __NL80211_PMSR_PEER_ATTR_INVALID, + + NL80211_PMSR_PEER_ATTR_ADDR, + NL80211_PMSR_PEER_ATTR_CHAN, + NL80211_PMSR_PEER_ATTR_REQ, + NL80211_PMSR_PEER_ATTR_RESP, + + /* keep last */ + NUM_NL80211_PMSR_PEER_ATTRS, + NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1, +}; + +/** + * enum nl80211_peer_measurement_attrs - peer measurement attributes + * @__NL80211_PMSR_ATTR_INVALID: invalid + * + * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability + * advertisement only, indicates the maximum number of peers + * measurements can be done with in a single request + * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability + * indicating that the connected AP's TSF can be reported in + * measurement results + * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability + * indicating that MAC address randomization is supported. + * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device, + * this contains a nesting indexed by measurement type, and + * type-specific capabilities inside, which are from the enums + * named nl80211_peer_measurement__capa. + * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is + * meaningless, just a list of peers to measure with, with the + * sub-attributes taken from + * &enum nl80211_peer_measurement_peer_attrs. + * + * @NUM_NL80211_PMSR_ATTR: internal + * @NL80211_PMSR_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_attrs { + __NL80211_PMSR_ATTR_INVALID, + + NL80211_PMSR_ATTR_MAX_PEERS, + NL80211_PMSR_ATTR_REPORT_AP_TSF, + NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, + NL80211_PMSR_ATTR_TYPE_CAPA, + NL80211_PMSR_ATTR_PEERS, + + /* keep last */ + NUM_NL80211_PMSR_ATTR, + NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_capa - FTM capabilities + * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode + * is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP + * mode is supported + * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI + * data can be requested during the measurement + * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic + * location data can be requested during the measurement + * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits + * from &enum nl80211_preamble. + * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from + * &enum nl80211_chan_width indicating the supported channel + * bandwidths for FTM. Note that a higher channel bandwidth may be + * configured to allow for other measurements types with different + * bandwidth requirement in the same measurement. + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating + * the maximum bursts exponent that can be used (if not present anything + * is valid) + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating + * the maximum FTMs per burst (if not present anything is valid) + * + * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal + * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_capa { + __NL80211_PMSR_FTM_CAPA_ATTR_INVALID, + + NL80211_PMSR_FTM_CAPA_ATTR_ASAP, + NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP, + NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI, + NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC, + NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES, + NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT, + NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, + + /* keep last */ + NUM_NL80211_PMSR_FTM_CAPA_ATTR, + NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_req - FTM request attributes + * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag) + * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see + * &enum nl80211_preamble), optional for DMG (u32) + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in + * 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element" + * (u8, 0-15, optional with default 15 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units + * of 100ms (u16, optional with default 0) + * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016 + * Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with + * default 15 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames + * requested per burst + * (u8, 0-31, optional with default 0 i.e. "no preference") + * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries + * (u8, default 3) + * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag) + * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data + * (flag) + * + * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal + * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_req { + __NL80211_PMSR_FTM_REQ_ATTR_INVALID, + + NL80211_PMSR_FTM_REQ_ATTR_ASAP, + NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, + NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, + NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, + NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, + NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, + NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES, + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI, + NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC, + + /* keep last */ + NUM_NL80211_PMSR_FTM_REQ_ATTR, + NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1 +}; + +/** + * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons + * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used + * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder + * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement + * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is + * on a different channel, so can't measure (if we didn't know, we'd + * try and get no response) + * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM + * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps + * received + * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry + * later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME) + * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed + * by the peer and are no longer supported + */ +enum nl80211_peer_measurement_ftm_failure_reasons { + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED, + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE, + NL80211_PMSR_FTM_FAILURE_REJECTED, + NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL, + NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE, + NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP, + NL80211_PMSR_FTM_FAILURE_PEER_BUSY, + NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS, +}; + +/** + * enum nl80211_peer_measurement_ftm_resp - FTM response attributes + * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid + * + * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason + * (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported + * as separate results then it will be the burst index 0...(N-1) and + * the top level will indicate partial results (u32) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames + * transmitted (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames + * that were acknowleged (u32, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the + * busy peer (u32, seconds) + * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent + * used by the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by + * the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used + * by the responder (similar to request, u8) + * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action + * frames (optional, s32, 1/2 dBm) + * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action + * frames (optional, s32, 1/2 dBm) + * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the + * FTM action frame (optional, nested, using &enum nl80211_rate_info + * attributes) + * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM + * action frame (optional, nested, using &enum nl80211_rate_info attrs) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional + * but one of RTT/DIST must be present) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that + * standard deviation is the square root of variance, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds, + * optional) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional + * but one of RTT/DIST must be present) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note + * that standard deviation is the square root of variance, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional) + * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional); + * this is the contents of the Measurement Report Element (802.11-2016 + * 9.4.2.22.1) starting with the Measurement Token, with Measurement + * Type 8. + * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer + * (binary, optional); + * this is the contents of the Measurement Report Element (802.11-2016 + * 9.4.2.22.1) starting with the Measurement Token, with Measurement + * Type 11. + * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only + * + * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal + * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number + */ +enum nl80211_peer_measurement_ftm_resp { + __NL80211_PMSR_FTM_RESP_ATTR_INVALID, + + NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, + NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX, + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS, + NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES, + NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME, + NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP, + NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION, + NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST, + NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG, + NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_TX_RATE, + NL80211_PMSR_FTM_RESP_ATTR_RX_RATE, + NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG, + NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE, + NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG, + NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE, + NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD, + NL80211_PMSR_FTM_RESP_ATTR_LCI, + NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, + NL80211_PMSR_FTM_RESP_ATTR_PAD, + + /* keep last */ + NUM_NL80211_PMSR_FTM_RESP_ATTR, + NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index bfe8811e33f2..438baf190be7 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -399,7 +399,7 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ len = dh->prime_len; if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, - dhpriv, dh->prime_len, peer_pub, + NULL, 0, dhpriv, dh->prime_len, peer_pub, dh->prime_len, modexp, &len) < 0) return -1; if (len < dh->prime_len) { diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 88c6595887ab..884150e6c1eb 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -8,11 +8,15 @@ #include "includes.h" #include "common.h" +#include "utils/const_time.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "eap_defs.h" #include "eap_pwd_common.h" +#define MAX_ECC_PRIME_LEN 66 + + /* The random function H(x) = HMAC-SHA256(0^32, x) */ struct crypto_hash * eap_pwd_h_init(void) { @@ -81,10 +85,23 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } +static int eap_pwd_suitable_group(u16 num) +{ + /* Do not allow ECC groups with prime under 256 bits based on guidance + * for the similar design in SAE. */ + return num == 19 || num == 20 || num == 21 || + num == 28 || num == 29 || num == 30; +} + + EAP_PWD_group * get_eap_pwd_group(u16 num) { EAP_PWD_group *grp; + if (!eap_pwd_suitable_group(num)) { + wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num); + return NULL; + } grp = os_zalloc(sizeof(EAP_PWD_group)); if (!grp) return NULL; @@ -102,6 +119,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 num) } +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; +} + + /* * compute a "random" secret point on an elliptic curve based * on the password and identities. @@ -113,33 +139,37 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *token) { struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL; + struct crypto_bignum *qr_or_qnr = NULL; + u8 qr_bin[MAX_ECC_PRIME_LEN]; + u8 qnr_bin[MAX_ECC_PRIME_LEN]; + u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN]; + u8 x_bin[MAX_ECC_PRIME_LEN]; struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; - int is_odd, ret = 0, check, found = 0; - size_t primebytelen, primebitlen; - struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + int ret = 0, check, res; + u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* + * mask */ + size_t primebytelen = 0, primebitlen; + struct crypto_bignum *x_candidate = NULL; const struct crypto_bignum *prime; + u8 mask, found_ctr = 0, is_odd = 0; if (grp->pwe) return -1; + os_memset(x_bin, 0, sizeof(x_bin)); + prime = crypto_ec_get_prime(grp->group); - cofactor = crypto_bignum_init(); grp->pwe = crypto_ec_point_init(grp->group); tmp1 = crypto_bignum_init(); pm1 = crypto_bignum_init(); one = crypto_bignum_init_set((const u8 *) "\x01", 1); - if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) { + if (!grp->pwe || !tmp1 || !pm1 || !one) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } - if (crypto_ec_cofactor(grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " - "curve"); - goto fail; - } primebitlen = crypto_ec_prime_len_bits(grp->group); primebytelen = crypto_ec_prime_len(grp->group); if ((prfbuf = os_malloc(primebytelen)) == NULL) { @@ -152,8 +182,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, /* get a random quadratic residue and nonresidue */ while (!qr || !qnr) { - int res; - if (crypto_bignum_rand(tmp1, prime) < 0) goto fail; res = crypto_bignum_legendre(tmp1, prime); @@ -167,6 +195,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, if (!tmp1) goto fail; } + if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), + primebytelen) < 0 || + crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), + primebytelen) < 0) + goto fail; os_memset(prfbuf, 0, primebytelen); ctr = 0; @@ -194,17 +227,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, eap_pwd_h_update(hash, &ctr, sizeof(ctr)); eap_pwd_h_final(hash, pwe_digest); - crypto_bignum_deinit(rnd, 1); - rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN); - if (!rnd) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd"); - goto fail; - } + is_odd = const_time_select_u8( + found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01); if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, (u8 *) "EAP-pwd Hunting And Pecking", os_strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) < 0) goto fail; + if (primebitlen % 8) + buf_shift_right(prfbuf, primebytelen, + 8 - primebitlen % 8); crypto_bignum_deinit(x_candidate, 1); x_candidate = crypto_bignum_init_set(prfbuf, primebytelen); @@ -214,30 +246,20 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, goto fail; } - /* - * eap_pwd_kdf() returns a string of bits 0..primebitlen but - * BN_bin2bn will treat that string of bits as a big endian - * number. If the primebitlen is not an even multiple of 8 - * then excessive bits-- those _after_ primebitlen-- so now - * we have to shift right the amount we masked off. - */ - if ((primebitlen % 8) && - crypto_bignum_rshift(x_candidate, - (8 - (primebitlen % 8)), - x_candidate) < 0) - goto fail; - if (crypto_bignum_cmp(x_candidate, prime) >= 0) continue; - wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", - prfbuf, primebytelen); + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate", + prfbuf, primebytelen); + const_time_select_bin(found, x_bin, prfbuf, primebytelen, + x_bin); /* * compute y^2 using the equation of the curve * * y^2 = x^3 + ax + b */ + crypto_bignum_deinit(tmp2, 1); tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate); if (!tmp2) goto fail; @@ -260,13 +282,15 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * Flip a coin, multiply by the random quadratic residue or the * random quadratic nonresidue and record heads or tails. */ - if (crypto_bignum_is_odd(tmp1)) { - crypto_bignum_mulmod(tmp2, qr, prime, tmp2); - check = 1; - } else { - crypto_bignum_mulmod(tmp2, qnr, prime, tmp2); - check = -1; - } + mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1); + check = const_time_select_s8(mask, 1, -1); + const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen, + qr_or_qnr_bin); + crypto_bignum_deinit(qr_or_qnr, 1); + qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen); + if (!qr_or_qnr || + crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0) + goto fail; /* * Now it's safe to do legendre, if check is 1 then it's @@ -274,59 +298,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * change result), if check is -1 then it's the opposite test * (multiplying a qr by qnr would make a qnr). */ - if (crypto_bignum_legendre(tmp2, prime) == check) { - if (found == 1) - continue; - - /* need to unambiguously identify the solution */ - is_odd = crypto_bignum_is_odd(rnd); - - /* - * We know x_candidate is a quadratic residue so set - * it here. - */ - if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe, - x_candidate, - is_odd) != 0) { - wpa_printf(MSG_INFO, - "EAP-pwd: Could not solve for y"); - continue; - } - - /* - * If there's a solution to the equation then the point - * must be on the curve so why check again explicitly? - * OpenSSL code says this is required by X9.62. We're - * not X9.62 but it can't hurt just to be sure. - */ - if (!crypto_ec_point_is_on_curve(grp->group, - grp->pwe)) { - wpa_printf(MSG_INFO, - "EAP-pwd: point is not on curve"); - continue; - } - - if (!crypto_bignum_is_one(cofactor)) { - /* make sure the point is not in a small - * sub-group */ - if (crypto_ec_point_mul(grp->group, grp->pwe, - cofactor, - grp->pwe) != 0) { - wpa_printf(MSG_INFO, - "EAP-pwd: cannot multiply generator by order"); - continue; - } - if (crypto_ec_point_is_at_infinity(grp->group, - grp->pwe)) { - wpa_printf(MSG_INFO, - "EAP-pwd: point is at infinity"); - continue; - } - } - wpa_printf(MSG_DEBUG, - "EAP-pwd: found a PWE in %d tries", ctr); - found = 1; - } + res = crypto_bignum_legendre(tmp2, prime); + if (res == -2) + goto fail; + mask = const_time_eq(res, check); + found_ctr = const_time_select_u8(found, found_ctr, ctr); + found |= mask; } if (found == 0) { wpa_printf(MSG_INFO, @@ -334,6 +311,31 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, num); goto fail; } + + /* + * We know x_candidate is a quadratic residue so set it here. + */ + crypto_bignum_deinit(x_candidate, 1); + x_candidate = crypto_bignum_init_set(x_bin, primebytelen); + if (!x_candidate || + crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate, + is_odd) != 0) { + wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y"); + goto fail; + } + + /* + * If there's a solution to the equation then the point must be on the + * curve so why check again explicitly? OpenSSL code says this is + * required by X9.62. We're not X9.62 but it can't hurt just to be sure. + */ + if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr); + if (0) { fail: crypto_ec_point_deinit(grp->pwe, 1); @@ -341,16 +343,19 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, ret = 1; } /* cleanliness and order.... */ - crypto_bignum_deinit(cofactor, 1); crypto_bignum_deinit(x_candidate, 1); - crypto_bignum_deinit(rnd, 1); crypto_bignum_deinit(pm1, 0); crypto_bignum_deinit(tmp1, 1); crypto_bignum_deinit(tmp2, 1); crypto_bignum_deinit(qr, 1); crypto_bignum_deinit(qnr, 1); + crypto_bignum_deinit(qr_or_qnr, 1); crypto_bignum_deinit(one, 0); - os_free(prfbuf); + bin_clear_free(prfbuf, primebytelen); + os_memset(qr_bin, 0, sizeof(qr_bin)); + os_memset(qnr_bin, 0, sizeof(qnr_bin)); + os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin)); + os_memset(pwe_digest, 0, sizeof(pwe_digest)); return ret; } @@ -416,3 +421,108 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, return 1; } + + +static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime, + const u8 *buf, size_t len) +{ + struct crypto_bignum *val; + int ok = 1; + + val = crypto_bignum_init_set(buf, len); + if (!val || crypto_bignum_is_zero(val) || + crypto_bignum_cmp(val, prime) >= 0) + ok = 0; + crypto_bignum_deinit(val, 0); + return ok; +} + + +struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group, + const u8 *buf) +{ + struct crypto_ec_point *element; + const struct crypto_bignum *prime; + size_t prime_len; + + prime = crypto_ec_get_prime(group->group); + prime_len = crypto_ec_prime_len(group->group); + + /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */ + if (!eap_pwd_element_coord_ok(prime, buf, prime_len) || + !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element"); + return NULL; + } + + element = crypto_ec_point_from_bin(group->group, buf); + if (!element) { + wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed"); + return NULL; + } + + /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */ + if (!crypto_ec_point_is_on_curve(group->group, element) || + crypto_ec_point_is_at_infinity(group->group, element)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid element"); + goto fail; + } + +out: + return element; +fail: + crypto_ec_point_deinit(element, 0); + element = NULL; + goto out; +} + + +struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf) +{ + struct crypto_bignum *scalar; + const struct crypto_bignum *order; + size_t order_len; + + order = crypto_ec_get_order(group->group); + order_len = crypto_ec_order_len(group->group); + + /* RFC 5931, 2.8.5.2: 1 < scalar < r */ + scalar = crypto_bignum_init_set(buf, order_len); + if (!scalar || crypto_bignum_is_zero(scalar) || + crypto_bignum_is_one(scalar) || + crypto_bignum_cmp(scalar, order) >= 0) { + wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid"); + crypto_bignum_deinit(scalar, 0); + scalar = NULL; + } + + return scalar; +} + + +int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar) +{ + const struct crypto_bignum *order; + int count; + + order = crypto_ec_get_order(group->group); + + /* Select two random values rand,mask such that 1 < rand,mask < r and + * rand + mask mod r > 1. */ + for (count = 0; count < 100; count++) { + if (crypto_bignum_rand(_rand, order) == 0 && + !crypto_bignum_is_zero(_rand) && + crypto_bignum_rand(_mask, order) == 0 && + !crypto_bignum_is_zero(_mask) && + crypto_bignum_add(_rand, _mask, scalar) == 0 && + crypto_bignum_mod(scalar, order, scalar) == 0 && + !crypto_bignum_is_zero(scalar) && + !crypto_bignum_is_one(scalar)) + return 0; + } + + wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness"); + return -1; +} diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index 6b07cf8f797c..c48acee204d3 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -67,5 +67,11 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, 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); +struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group, + const u8 *buf); +struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf); +int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand, + struct crypto_bignum *_mask, + struct crypto_bignum *scalar); #endif /* EAP_PWD_COMMON_H */ diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c index 8819541b2264..8ee9e32e1e48 100644 --- a/src/eap_common/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -201,14 +201,15 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len, * @data2_len: Length of the data2 * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success or -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i. */ -static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, - const u8 *data2, size_t data2_len, - u8 *buf, size_t buf_len) +static int eap_sake_kdf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len, + u8 *buf, size_t buf_len) { u8 counter = 0; size_t pos, plen; @@ -230,17 +231,21 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { - hmac_sha1_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA1_MAC_LEN; } else { - hmac_sha1_vector(key, key_len, 4, addr, len, - hash); + if (hmac_sha1_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); break; } counter++; } + + return 0; } @@ -253,12 +258,13 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16]) * @msk: Buffer for 64-byte MSK * @emsk: Buffer for 64-byte EMSK + * Returns: 0 on success or -1 on failure * * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6. */ -void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, - const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, - u8 *emsk) +int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) { u8 sms_a[EAP_SAKE_SMS_LEN]; u8 sms_b[EAP_SAKE_SMS_LEN]; @@ -268,14 +274,16 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A", root_secret_a, EAP_SAKE_ROOT_SECRET_LEN); - eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, - "SAKE Master Secret A", - rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, - sms_a, EAP_SAKE_SMS_LEN); + if (eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret A", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_a, EAP_SAKE_SMS_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN); - eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", - rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, - tek, EAP_SAKE_TEK_LEN); + if (eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + tek, EAP_SAKE_TEK_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth", tek, EAP_SAKE_TEK_AUTH_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher", @@ -283,18 +291,21 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B", root_secret_b, EAP_SAKE_ROOT_SECRET_LEN); - eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, - "SAKE Master Secret B", - rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, - sms_b, EAP_SAKE_SMS_LEN); + if (eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret B", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_b, EAP_SAKE_SMS_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN); - eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", - rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, - key_buf, sizeof(key_buf)); + if (eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + key_buf, sizeof(key_buf)) < 0) + return -1; os_memcpy(msk, key_buf, EAP_MSK_LEN); os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN); + return 0; } @@ -312,6 +323,7 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, * @eap_len: EAP packet length * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len]) * @mic: Buffer for the computed 16-byte MIC + * Returns: 0 on success or -1 on failure */ int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, @@ -323,6 +335,7 @@ int eap_sake_compute_mic(const u8 *tek_auth, u8 _rand[2 * EAP_SAKE_RAND_LEN]; u8 *tmp, *pos; size_t tmplen; + int ret; tmplen = serverid_len + 1 + peerid_len + 1 + eap_len; tmp = os_malloc(tmplen); @@ -364,14 +377,14 @@ int eap_sake_compute_mic(const u8 *tek_auth, os_memcpy(pos, eap, eap_len); os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN); - eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, - peer ? "Peer MIC" : "Server MIC", - _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, - mic, EAP_SAKE_MIC_LEN); + ret = eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, + peer ? "Peer MIC" : "Server MIC", + _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, + mic, EAP_SAKE_MIC_LEN); os_free(tmp); - return 0; + return ret; } diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h index 9e1e75745a11..a817a35d438c 100644 --- a/src/eap_common/eap_sake_common.h +++ b/src/eap_common/eap_sake_common.h @@ -1,6 +1,6 @@ /* * EAP server/peer: EAP-SAKE shared routines - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -81,9 +81,9 @@ struct eap_sake_parse_attr { int eap_sake_parse_attributes(const u8 *buf, size_t len, struct eap_sake_parse_attr *attr); -void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, - const u8 *rand_s, const u8 *rand_p, - u8 *tek, u8 *msk, u8 *emsk); +int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, + u8 *tek, u8 *msk, u8 *emsk); int eap_sake_compute_mic(const u8 *tek_auth, const u8 *rand_s, const u8 *rand_p, const u8 *serverid, size_t serverid_len, diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index d416afd56d59..3a88f2abd236 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -101,7 +101,7 @@ struct eap_peer_config { * certificate store (My user account) is used, whereas computer store * (Computer account) is used when running wpasvc as a service. */ - u8 *ca_cert; + char *ca_cert; /** * ca_path - Directory path for CA certificate files (PEM) @@ -112,7 +112,7 @@ struct eap_peer_config { * these certificates are added to the list of trusted CAs. ca_cert * may also be included in that case, but it is not required. */ - u8 *ca_path; + char *ca_path; /** * client_cert - File path to client certificate file (PEM/DER) @@ -126,7 +126,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *client_cert; + char *client_cert; /** * private_key - File path to client private key file (PEM/DER/PFX) @@ -153,7 +153,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *private_key; + char *private_key; /** * private_key_passwd - Password for private key file @@ -178,7 +178,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *dh_file; + char *dh_file; /** * subject_match - Constraint for server certificate subject @@ -194,7 +194,49 @@ struct eap_peer_config { * 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; + char *subject_match; + + /** + * check_cert_subject - Constraint for server certificate subject fields + * + * If check_cert_subject is set, the value of every field will be + * checked against the DN of the subject in the authentication server + * certificate. If the values do not match, the certificate verification + * will fail, rejecting the server. This option allows wpa_supplicant to + * match every individual field in the right order against the DN of the + * subject in the server certificate. + * + * For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will + * check every individual DN field of the subject in the server + * certificate. If OU=XYZ comes first in terms of the order in the + * server certificate (DN field of server certificate + * C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), wpa_supplicant will reject the + * server because the order of 'OU' is not matching the specified string + * in check_cert_subject. + * + * This option also allows '*' as a wildcard. This option has some + * limitation. + * It can only be used as per the following example. + * + * For example, check_cert_subject=C=US/O=XX/OU=Production* and we have + * two servers and DN of the subject in the first server certificate is + * (C=US/O=XX/OU=Production Unit) and DN of the subject in the second + * server is (C=US/O=XX/OU=Production Factory). In this case, + * wpa_supplicant will allow both servers because the value of 'OU' + * field in both server certificates matches 'OU' value in + * 'check_cert_subject' up to 'wildcard'. + * + * (Allow all servers, e.g., check_cert_subject=*) + */ + char *check_cert_subject; + + /** + * check_cert_subject2 - Constraint for server certificate subject fields + * + * This field is like check_cert_subject, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *check_cert_subject2; /** * altsubject_match - Constraint for server certificate alt. subject @@ -212,23 +254,26 @@ struct eap_peer_config { * * Following types are supported: EMAIL, DNS, URI */ - u8 *altsubject_match; + char *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. + * If set, this semicolon deliminated list of FQDNs is used as suffix + * match requirements for the server certificate in SubjectAltName + * dNSName element(s). If a matching dNSName is found against any of the + * specified values, 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 case-insentively 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. + * test.example.com but would not match test-example.com. Multiple + * match options can be specified in following manner: + * example.org;example.com. */ char *domain_suffix_match; @@ -244,6 +289,12 @@ struct eap_peer_config { * 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". + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. */ char *domain_match; @@ -263,7 +314,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *ca_cert2; + char *ca_cert2; /** * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) @@ -277,7 +328,7 @@ struct eap_peer_config { * This field is like ca_path, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *ca_path2; + char *ca_path2; /** * client_cert2 - File path to client certificate file @@ -290,7 +341,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *client_cert2; + char *client_cert2; /** * private_key2 - File path to client private key file @@ -303,7 +354,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *private_key2; + char *private_key2; /** * private_key2_passwd - Password for private key file @@ -324,7 +375,7 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. */ - u8 *dh_file2; + char *dh_file2; /** * subject_match2 - Constraint for server certificate subject @@ -332,7 +383,7 @@ struct eap_peer_config { * This field is like subject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *subject_match2; + char *subject_match2; /** * altsubject_match2 - Constraint for server certificate alt. subject @@ -340,7 +391,7 @@ struct eap_peer_config { * This field is like altsubject_match, but used for phase 2 (inside * EAP-TTLS/PEAP/FAST tunnel) authentication. */ - u8 *altsubject_match2; + char *altsubject_match2; /** * domain_suffix_match2 - Constraint for server domain name diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 74cec7dd583f..94ce57d6219f 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -250,8 +250,8 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) 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); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); os_free(data); } @@ -486,7 +486,7 @@ static int eap_fast_phase2_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim)) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) return -1; @@ -801,7 +801,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; data->phase2_success = 0; - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } @@ -815,7 +815,7 @@ static struct wpabuf * eap_fast_process_crypto_binding( } else { wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " "Session-Id"); - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } } @@ -1150,7 +1150,7 @@ static int eap_fast_encrypt_response(struct eap_sm *sm, wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " "frame"); } - wpabuf_free(resp); + wpabuf_clear_free(resp); return 0; } @@ -1328,14 +1328,14 @@ static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " "TLV frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return -1; } res = eap_fast_process_decrypted(sm, data, ret, identifier, in_decrypted, out_data); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return res; } @@ -1613,7 +1613,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-FAST: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = resp; return NULL; } @@ -1641,7 +1641,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, "EAP-FAST: Could not derive keys"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(resp); + wpabuf_clear_free(resp); return NULL; } } @@ -1650,7 +1650,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp); @@ -1658,7 +1658,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, } if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_FAST, data->fast_version); } @@ -1684,9 +1684,9 @@ static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); os_free(data->key_block_p); data->key_block_p = NULL; - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; } diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 877495cf3ac7..249baec88ebb 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -856,9 +856,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); - get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 0, 0); + if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, + 0) < 0 || + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, key_len); diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 34075b1d9af6..8dcf7cc2926f 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -169,7 +169,7 @@ 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); + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); data->key_data = NULL; } } @@ -186,9 +186,9 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_peap_free_key(data); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); - os_free(data); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); + bin_clear_free(data, sizeof(*data)); } @@ -253,7 +253,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; - int resumed; + int resumed, res; /* * Tunnel key (TK) is the first 60 octets of the key generated by @@ -292,9 +292,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - if (peap_prfplus(data->peap_version, tk, 40, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)) < 0) + res = peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + os_memset(isk, 0, sizeof(isk)); + if (res < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -303,6 +305,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + os_memset(imck, 0, sizeof(imck)); return 0; } @@ -382,7 +385,7 @@ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, wpabuf_put_be16(msg, status); /* Status */ if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { - wpabuf_free(msg); + wpabuf_clear_free(msg); return NULL; } @@ -651,11 +654,11 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(buf); + wpabuf_clear_free(buf); return -1; } wpabuf_put_buf(*resp, buf); - wpabuf_free(buf); + wpabuf_clear_free(buf); break; } } @@ -728,7 +731,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim)) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } @@ -807,7 +810,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); if (nmsg == NULL) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); @@ -817,7 +820,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, nhdr->length = host_to_be16(sizeof(struct eap_hdr) + wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); in_decrypted = nmsg; } @@ -826,7 +829,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " "EAP frame (len=%lu)", (unsigned long) wpabuf_len(in_decrypted)); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } len = be_to_host16(hdr->length); @@ -835,7 +838,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, "Phase 2 EAP frame (len=%lu hdr->length=%lu)", (unsigned long) wpabuf_len(in_decrypted), (unsigned long) len); - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } if (len < wpabuf_len(in_decrypted)) { @@ -852,7 +855,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, case EAP_CODE_REQUEST: if (eap_peap_phase2_request(sm, data, ret, in_decrypted, &resp)) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " "processing failed"); return 0; @@ -872,7 +875,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, "completed successfully"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return 0; } wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " @@ -882,7 +885,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, ret->methodState = METHOD_DONE; data->phase2_success = 1; if (data->peap_outer_success == 2) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " "to finish authentication"); return 1; @@ -928,7 +931,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, break; } - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); if (resp) { int skip_change2 = 0; @@ -955,7 +958,7 @@ static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " "a Phase 2 frame"); } - wpabuf_free(resp); + wpabuf_clear_free(resp); } return 0; @@ -1056,7 +1059,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = resp; return NULL; } @@ -1081,12 +1084,19 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, "key derivation", label); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, - EAP_TLS_KEY_LEN); + NULL, 0, + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); if (data->key_data) { wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Derived key", data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived EMSK", + data->key_data + + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " "derive key"); @@ -1131,7 +1141,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = resp; resp = NULL; res = eap_peap_decrypt(sm, data, ret, req, &msg, @@ -1144,7 +1154,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, } if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, data->peap_version); } @@ -1168,9 +1178,9 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) if (data->phase2_priv && data->phase2_method && data->phase2_method->deinit_for_reauth) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; data->crypto_binding_used = 0; } @@ -1257,6 +1267,7 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) os_memcpy(key, csk, EAP_TLS_KEY_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", key, EAP_TLS_KEY_LEN); + os_memset(csk, 0, sizeof(csk)); } else os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); @@ -1264,6 +1275,29 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (!data->key_data || !data->phase2_success) + return NULL; + + if (data->crypto_binding_used) { + /* [MS-PEAP] does not define EMSK derivation */ + return NULL; + } + + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + if (!key) + return NULL; + + *len = EAP_EMSK_LEN; + + return key; +} + + static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) { struct eap_peap_data *data = priv; @@ -1296,6 +1330,7 @@ int eap_peer_peap_register(void) eap->process = eap_peap_process; eap->isKeyAvailable = eap_peap_isKeyAvailable; eap->getKey = eap_peap_getKey; + eap->get_emsk = eap_peap_get_emsk; eap->get_status = eap_peap_get_status; eap->has_reauth_data = eap_peap_has_reauth_data; eap->deinit_for_reauth = eap_peap_deinit_for_reauth; diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 761c16af996a..76fcad4a50e0 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -308,10 +308,10 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - struct crypto_ec_point *K = NULL, *point = NULL; - struct crypto_bignum *mask = NULL, *cofactor = NULL; + struct crypto_ec_point *K = NULL; + struct crypto_bignum *mask = NULL; const u8 *ptr = payload; - u8 *scalar = NULL, *element = NULL; + u8 *scalar, *element; size_t prime_len, order_len; const u8 *password; size_t password_len; @@ -527,34 +527,17 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, data->private_value = crypto_bignum_init(); data->my_element = crypto_ec_point_init(data->grp->group); - cofactor = crypto_bignum_init(); data->my_scalar = crypto_bignum_init(); mask = crypto_bignum_init(); - if (!data->private_value || !data->my_element || !cofactor || + if (!data->private_value || !data->my_element || !data->my_scalar || !mask) { wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); goto fin; } - if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " - "for curve"); + if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, + data->my_scalar) < 0) goto fin; - } - - if (crypto_bignum_rand(data->private_value, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_rand(mask, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_add(data->private_value, mask, - data->my_scalar) < 0 || - crypto_bignum_mod(data->my_scalar, - crypto_ec_get_order(data->grp->group), - data->my_scalar) < 0) { - wpa_printf(MSG_INFO, - "EAP-pwd (peer): unable to get randomness"); - goto fin; - } if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, data->my_element) < 0) { @@ -572,43 +555,27 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, /* process the request */ data->k = crypto_bignum_init(); K = crypto_ec_point_init(data->grp->group); - point = crypto_ec_point_init(data->grp->group); - if (!data->k || !K || !point) { + if (!data->k || !K) { wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " "fail"); goto fin; } /* element, x then y, followed by scalar */ - data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr); + data->server_element = eap_pwd_get_element(data->grp, ptr); if (!data->server_element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " "fail"); goto fin; } ptr += prime_len * 2; - data->server_scalar = crypto_bignum_init_set(ptr, order_len); + data->server_scalar = eap_pwd_get_scalar(data->grp, ptr); if (!data->server_scalar) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer scalar fail"); goto fin; } - /* check to ensure server's element is not in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, data->server_element, - cofactor, point) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " - "server element by order!\n"); - goto fin; - } - if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " - "is at infinity!\n"); - goto fin; - } - } - /* compute the shared key, k */ if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, data->server_scalar, K) < 0 || @@ -621,17 +588,8 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - /* ensure that the shared key isn't in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " - "shared key point by order"); - goto fin; - } - } - /* - * This check is strictly speaking just for the case above where + * This check is strictly speaking just for the case where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. @@ -649,12 +607,12 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } /* now do the response */ - scalar = os_zalloc(order_len); - element = os_zalloc(prime_len * 2); - if (!scalar || !element) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); + data->outbuf = wpabuf_alloc(2 * prime_len + order_len); + if (data->outbuf == NULL) goto fin; - } + /* We send the element as (x,y) followed by the scalar */ + element = wpabuf_put(data->outbuf, 2 * prime_len); + scalar = wpabuf_put(data->outbuf, order_len); /* * bignums occupy as little memory as possible so one that is @@ -668,21 +626,9 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - data->outbuf = wpabuf_alloc(order_len + 2 * prime_len); - if (data->outbuf == NULL) - goto fin; - - /* we send the element as (x,y) follwed by the scalar */ - wpabuf_put_data(data->outbuf, element, 2 * prime_len); - wpabuf_put_data(data->outbuf, scalar, order_len); - fin: - os_free(scalar); - os_free(element); crypto_bignum_deinit(mask, 1); - crypto_bignum_deinit(cofactor, 1); crypto_ec_point_deinit(K, 1); - crypto_ec_point_deinit(point, 1); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else @@ -986,6 +932,13 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, * buffer and ACK the fragment */ if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { + if (!data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: No buffer for reassembly"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " @@ -1012,7 +965,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, /* * we're buffering and this is the last fragment */ - if (data->in_frag_pos) { + if (data->in_frag_pos && data->inbuf) { wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); pos = wpabuf_head_u8(data->inbuf); diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index 0a6ce255af4d..255241f6d5aa 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-SAKE (RFC 4763) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -235,9 +235,13 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, data->serverid_len = attr.serverid_len; } - eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, - data->rand_s, data->rand_p, - (u8 *) &data->tek, data->msk, data->emsk); + if (eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, + data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); + return NULL; + } wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index cb747026cb8a..ffea9d213855 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -198,6 +198,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, eap_tls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 0de131526a51..cb94c452efce 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-2013, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -70,16 +70,22 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; if (os_strstr(txt, "tls_disable_tlsv1_0=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_0; - if (os_strstr(txt, "tls_disable_tlsv1_0=0")) + if (os_strstr(txt, "tls_disable_tlsv1_0=0")) { params->flags &= ~TLS_CONN_DISABLE_TLSv1_0; + params->flags |= TLS_CONN_ENABLE_TLSv1_0; + } if (os_strstr(txt, "tls_disable_tlsv1_1=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_1; - if (os_strstr(txt, "tls_disable_tlsv1_1=0")) + if (os_strstr(txt, "tls_disable_tlsv1_1=0")) { params->flags &= ~TLS_CONN_DISABLE_TLSv1_1; + params->flags |= TLS_CONN_ENABLE_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")) + if (os_strstr(txt, "tls_disable_tlsv1_2=0")) { params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; + params->flags |= TLS_CONN_ENABLE_TLSv1_2; + } if (os_strstr(txt, "tls_disable_tlsv1_3=1")) params->flags |= TLS_CONN_DISABLE_TLSv1_3; if (os_strstr(txt, "tls_disable_tlsv1_3=0")) @@ -102,14 +108,15 @@ static void eap_tls_params_flags(struct tls_connection_params *params, static void eap_tls_params_from_conf1(struct tls_connection_params *params, struct eap_peer_config *config) { - params->ca_cert = (char *) config->ca_cert; - params->ca_path = (char *) config->ca_path; - params->client_cert = (char *) config->client_cert; - params->private_key = (char *) config->private_key; - params->private_key_passwd = (char *) config->private_key_passwd; - params->dh_file = (char *) config->dh_file; - params->subject_match = (char *) config->subject_match; - params->altsubject_match = (char *) config->altsubject_match; + params->ca_cert = config->ca_cert; + params->ca_path = config->ca_path; + params->client_cert = config->client_cert; + params->private_key = config->private_key; + params->private_key_passwd = config->private_key_passwd; + params->dh_file = config->dh_file; + params->subject_match = config->subject_match; + params->altsubject_match = config->altsubject_match; + params->check_cert_subject = config->check_cert_subject; params->suffix_match = config->domain_suffix_match; params->domain_match = config->domain_match; params->engine = config->engine; @@ -125,14 +132,15 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, static void eap_tls_params_from_conf2(struct tls_connection_params *params, struct eap_peer_config *config) { - params->ca_cert = (char *) config->ca_cert2; - params->ca_path = (char *) config->ca_path2; - params->client_cert = (char *) config->client_cert2; - params->private_key = (char *) config->private_key2; - params->private_key_passwd = (char *) config->private_key2_passwd; - params->dh_file = (char *) config->dh_file2; - params->subject_match = (char *) config->subject_match2; - params->altsubject_match = (char *) config->altsubject_match2; + params->ca_cert = config->ca_cert2; + params->ca_path = config->ca_path2; + params->client_cert = config->client_cert2; + params->private_key = config->private_key2; + params->private_key_passwd = config->private_key2_passwd; + params->dh_file = config->dh_file2; + params->subject_match = config->subject_match2; + params->altsubject_match = config->altsubject_match2; + params->check_cert_subject = config->check_cert_subject2; params->suffix_match = config->domain_suffix_match2; params->domain_match = config->domain_match2; params->engine = config->engine2; @@ -170,7 +178,9 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, * TLS v1.3 changes, so disable this by default for now. */ params->flags |= TLS_CONN_DISABLE_TLSv1_3; } - if (data->eap_type == EAP_TYPE_TLS) { + if (data->eap_type == EAP_TYPE_TLS || + data->eap_type == EAP_UNAUTH_TLS_TYPE || + data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) { /* While the current EAP-TLS implementation is more or less * complete for TLS v1.3, there has been no interoperability * testing with other implementations, so disable for by default @@ -339,6 +349,8 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @len: Length of the key material to generate (usually 64 for MSK) * Returns: Pointer to allocated key on success or %NULL on failure * @@ -347,9 +359,12 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) * different label to bind the key usage into the generated material. * * The caller is responsible for freeing the returned buffer. + * + * Note: To provide the RFC 5705 context, the context variable must be non-NULL. */ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len) + const char *label, const u8 *context, + size_t context_len, size_t len) { u8 *out; @@ -357,8 +372,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out, - len)) { + if (tls_connection_export_key(data->ssl_ctx, data->conn, label, + context, context_len, out, len)) { os_free(out); return NULL; } @@ -388,10 +403,26 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, u8 *out; if (eap_type == EAP_TYPE_TLS && data->tls_v13) { - *len = 64; - return eap_peer_tls_derive_key(sm, data, - "EXPORTER_EAP_TLS_Session-Id", - 64); + u8 *id, *method_id; + + /* Session-Id = || Method-Id + * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", + * "", 64) + */ + *len = 1 + 64; + id = os_malloc(*len); + if (!id) + return NULL; + method_id = eap_peer_tls_derive_key( + sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); + if (!method_id) { + os_free(id); + return NULL; + } + id[0] = eap_type; + os_memcpy(id + 1, method_id, 64); + os_free(method_id); + return id; } if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) || diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index 306e6a98bc3f..5f825294d787 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -99,7 +99,8 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, struct eap_peer_config *config, u8 eap_type); void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - const char *label, size_t len); + const char *label, const u8 *context, + size_t context_len, 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); diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index f18788ce8cb5..1c8dbe2b4331 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -196,8 +196,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_ttls_free_key(data); os_free(data->session_id); - wpabuf_free(data->pending_phase2_req); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_resp); os_free(data); } @@ -248,7 +248,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); if (msg == NULL) { - wpabuf_free(*resp); + wpabuf_clear_free(*resp); *resp = NULL; return -1; } @@ -258,7 +258,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); pos += wpabuf_len(*resp); AVP_PAD(avp, pos); - wpabuf_free(*resp); + wpabuf_clear_free(*resp); wpabuf_put(msg, pos - avp); *resp = msg; return 0; @@ -271,6 +271,7 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, eap_ttls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "ttls keying material", + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (!data->key_data) { @@ -303,7 +304,8 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { - return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", + NULL, 0, len); } #endif /* CONFIG_FIPS */ @@ -510,7 +512,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "implicit challenge"); return -1; @@ -529,7 +531,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, *pos++ = 0; /* Flags */ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { os_free(challenge); - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " "random data for peer challenge"); return -1; @@ -543,7 +545,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, peer_challenge, pos, data->auth_response, data->master_key)) { os_free(challenge); - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " "response"); return -1; @@ -604,7 +606,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " "implicit challenge"); return -1; @@ -628,7 +630,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, if (challenge_response(challenge, password, pos)) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed derive password hash"); - wpabuf_free(msg); + wpabuf_clear_free(msg); os_free(challenge); return -1; } @@ -641,7 +643,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, pos)) { wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed derive password"); - wpabuf_free(msg); + wpabuf_clear_free(msg); os_free(challenge); return -1; } @@ -760,7 +762,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, challenge = eap_ttls_implicit_challenge( sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (challenge == NULL) { - wpabuf_free(msg); + wpabuf_clear_free(msg); wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " "implicit challenge"); return -1; @@ -1073,10 +1075,10 @@ 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); + wpabuf_clear_free(resp); return -1; } - wpabuf_free(resp); + wpabuf_clear_free(resp); return 0; } @@ -1297,7 +1299,7 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, config->pending_req_otp || config->pending_req_new_password || config->pending_req_sim) { - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_dup(in_decrypted); } @@ -1340,7 +1342,7 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, * processing when EAP request is re-processed after * user input. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc(0); } @@ -1413,7 +1415,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, in_decrypted = data->pending_phase2_req; data->pending_phase2_req = NULL; if (wpabuf_len(in_decrypted) == 0) { - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); return eap_ttls_implicit_identity_request( sm, data, ret, identifier, out_data); } @@ -1449,7 +1451,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, &parse, in_decrypted, out_data); done: - wpabuf_free(in_decrypted); + wpabuf_clear_free(in_decrypted); os_free(parse.eapdata); if (retval < 0) { @@ -1509,7 +1511,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, if (sm->waiting_ext_cert_check) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Waiting external server certificate validation"); - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = *out_data; *out_data = NULL; return 0; @@ -1543,7 +1545,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, /* * Application data included in the handshake message. */ - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = *out_data; *out_data = NULL; res = eap_ttls_decrypt(sm, data, ret, identifier, in_data, @@ -1646,7 +1648,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, /* FIX: what about res == -1? Could just move all error processing into * the other functions and get rid of this res==1 case here. */ if (res == 1) { - wpabuf_free(resp); + wpabuf_clear_free(resp); return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, data->ttls_version); } @@ -1669,9 +1671,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) if (data->phase2_priv && data->phase2_method && data->phase2_method->deinit_for_reauth) data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); - wpabuf_free(data->pending_phase2_req); + wpabuf_clear_free(data->pending_phase2_req); data->pending_phase2_req = NULL; - wpabuf_free(data->pending_resp); + wpabuf_clear_free(data->pending_resp); data->pending_resp = NULL; data->decision_succ = DECISION_FAIL; #ifdef EAP_TNC diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index d140c88b8cdd..92d5a0235130 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -255,6 +255,9 @@ static void * eap_wsc_init(struct eap_sm *sm) cfg.new_ap_settings = &new_ap_settings; } + if (os_strstr(phase1, "multi_ap=1")) + cfg.multi_ap_backhaul_sta = 1; + data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 4fbc661c22fe..b130368b64da 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -153,11 +153,14 @@ void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); const char * eap_get_serial_num(struct eap_sm *sm); +const char * eap_get_method(struct eap_sm *sm); +const char * eap_get_imsi(struct eap_sm *sm); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, const u8 *username, size_t username_len, const u8 *challenge, const u8 *response); void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len); +void eap_user_free(struct eap_user *user); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index cf8a9f0d98e1..1cade10bee55 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -160,6 +160,7 @@ struct eap_sm { u8 *identity; size_t identity_len; char *serial_num; + char imsi[20]; /* Whether Phase 2 method should validate identity match */ int require_identity_match; int lastId; /* Identifier used in the last EAP-Packet */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 38a1b5c9ee22..e8b36e13380d 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -25,9 +25,6 @@ #define EAP_MAX_AUTH_ROUNDS 50 -static void eap_user_free(struct eap_user *user); - - /* EAP state machines are described in RFC 4137 */ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, @@ -1814,7 +1811,7 @@ int eap_server_sm_step(struct eap_sm *sm) } -static void eap_user_free(struct eap_user *user) +void eap_user_free(struct eap_user *user) { if (user == NULL) return; @@ -2003,6 +2000,32 @@ const char * eap_get_serial_num(struct eap_sm *sm) } +/** + * eap_get_method - Get the used EAP method + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the method name or %NULL if not available + */ +const char * eap_get_method(struct eap_sm *sm) +{ + if (!sm || !sm->m) + return NULL; + return sm->m->name; +} + + +/** + * eap_get_imsi - Get IMSI of the user + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to IMSI or %NULL if not available + */ +const char * eap_get_imsi(struct eap_sm *sm) +{ + if (!sm || sm->imsi[0] == '\0') + return NULL; + return sm->imsi; +} + + void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len) { #ifdef CONFIG_ERP diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 175021163c1d..1bea706d4990 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -796,6 +796,10 @@ static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) return; } + if (data->permanent[0] == EAP_AKA_PERMANENT_PREFIX || + data->permanent[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) + os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi)); + #ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) { /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index fb3d11748c8c..bebb17f40aaa 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -181,7 +181,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, data->specifier, start, pos - start, pos) < 0) { - os_free(req); + wpabuf_free(req); eap_gpsk_state(data, FAILURE); return NULL; } @@ -379,7 +379,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, data->specifier = WPA_GET_BE16(csuite->specifier); wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", data->vendor, data->specifier); - pos += sizeof(*csuite); + pos += sizeof(*csuite); if (end - pos < 2) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 6c47bb636aab..e9e03b0afb45 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -551,9 +551,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) if (key == NULL) return NULL; /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ - get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); - get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, - MSCHAPV2_KEY_LEN, 1, 1); + if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, + 1) < 0 || + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 1) < 0) { + os_free(key); + return NULL; + } wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); return key; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 3257789695cd..2e8c1a60c71f 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -107,9 +107,14 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, data->rand.r.x, EAP_PAX_RAND_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, (u8 *) "", 0, - wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, (u8 *) "", 0, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; @@ -144,18 +149,28 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, wpabuf_put_be16(req, EAP_PAX_MAC_LEN); pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, NULL, 0, pos); + if (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, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); /* Optional ADE could be added here, if needed */ pos = wpabuf_put(req, EAP_PAX_MAC_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos) < 0) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); + data->state = FAILURE; + wpabuf_free(req); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); return req; @@ -190,7 +205,7 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, u8 icvbuf[EAP_PAX_ICV_LEN], *icv; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); - if (pos == NULL || len < sizeof(*resp)) { + if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); return TRUE; } @@ -264,11 +279,11 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv, } icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_mhead(respData), - wpabuf_len(respData) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, icvbuf); - if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, icvbuf) < 0 || + 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); @@ -395,11 +410,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } data->keys_set = 1; - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.x, EAP_PAX_RAND_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, mac); - if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, mac) < 0 || + 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,11 +432,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm, return; } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_head(respData), - wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, - icvbuf); - if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, + NULL, 0, icvbuf) < 0 || + 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); diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 18d31b527fdd..92c0e5ec9716 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -324,13 +324,14 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) { u8 *tk; u8 isk[32], imck[60]; + int res; /* * Tunnel key (TK) is the first 60 octets of the key generated by * phase 1 of PEAP (based on TLS). */ tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", - EAP_TLS_KEY_LEN); + NULL, 0, EAP_TLS_KEY_LEN); if (tk == NULL) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); @@ -358,9 +359,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * in the end of the label just before ISK; is that just a typo?) */ wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); - if (peap_prfplus(data->peap_version, tk, 40, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)) < 0) { + res = peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + os_memset(isk, 0, sizeof(isk)); + if (res < 0) { os_free(tk); return -1; } @@ -373,6 +376,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + os_memset(imck, 0, sizeof(imck)); return 0; } @@ -756,7 +760,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, } else { eap_peap_state(data, FAILURE); } - + } else if (status == EAP_TLV_RESULT_FAILURE) { wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " "- requested %s", requested); @@ -1322,14 +1326,17 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) "key"); } + os_memset(csk, 0, sizeof(csk)); + return eapKeyData; } /* TODO: PEAPv1 - different label in some cases */ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); + "client EAP encryption", NULL, 0, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { + os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", eapKeyData, EAP_TLS_KEY_LEN); @@ -1341,6 +1348,40 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + if (data->crypto_binding_used) { + /* [MS-PEAP] does not define EMSK derivation */ + return NULL; + } + + /* TODO: PEAPv1 - different label in some cases */ + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", NULL, 0, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (!emsk) + return NULL; + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive EMSK"); + emsk = NULL; + } + + return emsk; +} + + static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; @@ -1376,6 +1417,7 @@ int eap_server_peap_register(void) eap->process = eap_peap_process; eap->isDone = eap_peap_isDone; eap->getKey = eap_peap_getKey; + eap->get_emsk = eap_peap_get_emsk; eap->isSuccess = eap_peap_isSuccess; eap->getSessionId = eap_peap_get_session_id; diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index d0fa54a3aba7..e720a28c85ba 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -236,7 +236,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { struct crypto_bignum *mask = NULL; - u8 *scalar = NULL, *element = NULL; + u8 *scalar, *element; size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); @@ -261,18 +261,9 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - if (crypto_bignum_rand(data->private_value, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_rand(mask, - crypto_ec_get_order(data->grp->group)) < 0 || - crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 || - crypto_bignum_mod(data->my_scalar, - crypto_ec_get_order(data->grp->group), - data->my_scalar) < 0) { - wpa_printf(MSG_INFO, - "EAP-pwd (server): unable to get randomness"); + if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask, + data->my_scalar) < 0) goto fin; - } if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, data->my_element) < 0) { @@ -288,22 +279,6 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, goto fin; } - scalar = os_malloc(order_len); - element = os_malloc(prime_len * 2); - if (!scalar || !element) { - wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); - goto fin; - } - - if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, - element + prime_len) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " - "fail"); - goto fin; - } - - crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); - data->outbuf = wpabuf_alloc(2 * prime_len + order_len + (data->salt ? 1 + data->salt_len : 0)); if (data->outbuf == NULL) @@ -316,13 +291,18 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, } /* We send the element as (x,y) followed by the scalar */ - wpabuf_put_data(data->outbuf, element, 2 * prime_len); - wpabuf_put_data(data->outbuf, scalar, order_len); + element = wpabuf_put(data->outbuf, 2 * prime_len); + scalar = wpabuf_put(data->outbuf, order_len); + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " + "fail"); + goto fin; + } fin: crypto_bignum_deinit(mask, 1); - os_free(scalar); - os_free(element); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -331,7 +311,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; size_t prime_len, order_len; @@ -412,6 +392,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, /* all done with the random function */ eap_pwd_h_final(hash, conf); + hash = NULL; os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); @@ -424,6 +405,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); + eap_pwd_h_final(hash, NULL); } @@ -668,8 +650,7 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { const u8 *ptr; - struct crypto_bignum *cofactor = NULL; - struct crypto_ec_point *K = NULL, *point = NULL; + struct crypto_ec_point *K = NULL; int res = 0; size_t prime_len, order_len; @@ -687,50 +668,36 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, } data->k = crypto_bignum_init(); - cofactor = crypto_bignum_init(); - point = crypto_ec_point_init(data->grp->group); K = crypto_ec_point_init(data->grp->group); - if (!data->k || !cofactor || !point || !K) { + if (!data->k || !K) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " - "cofactor for curve"); - goto fin; - } - /* element, x then y, followed by scalar */ ptr = payload; - data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr); + data->peer_element = eap_pwd_get_element(data->grp, ptr); if (!data->peer_element) { wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " "fail"); goto fin; } ptr += prime_len * 2; - data->peer_scalar = crypto_bignum_init_set(ptr, order_len); + data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr); if (!data->peer_scalar) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - /* check to ensure peer's element is not in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, data->peer_element, - cofactor, point) != 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " - "multiply peer element by order"); - goto fin; - } - if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " - "is at infinity!\n"); - goto fin; - } + /* detect reflection attacks */ + if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 || + crypto_ec_point_cmp(data->grp->group, data->my_element, + data->peer_element) == 0) { + wpa_printf(MSG_INFO, + "EAP-PWD (server): detected reflection attack!"); + goto fin; } /* compute the shared key, k */ @@ -745,18 +712,8 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - /* ensure that the shared key isn't in a small sub-group */ - if (!crypto_bignum_is_one(cofactor)) { - if (crypto_ec_point_mul(data->grp->group, K, cofactor, - K) != 0) { - wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " - "multiply shared key point by order!\n"); - goto fin; - } - } - /* - * This check is strictly speaking just for the case above where + * This check is strictly speaking just for the case where * co-factor > 1 but it was suggested that even though this is probably * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. @@ -775,8 +732,6 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, fin: crypto_ec_point_deinit(K, 1); - crypto_ec_point_deinit(point, 1); - crypto_bignum_deinit(cofactor, 1); if (res) eap_pwd_state(data, PWD_Confirm_Req); @@ -789,7 +744,7 @@ static void eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; @@ -864,6 +819,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, /* all done */ eap_pwd_h_final(hash, conf); + hash = NULL; ptr = (u8 *) payload; if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { @@ -883,6 +839,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, fin: bin_clear_free(cruft, prime_len * 2); + eap_pwd_h_final(hash, NULL); } @@ -955,6 +912,12 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * the first and all intermediate fragments have the M bit set */ if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { + if (!data->inbuf) { + wpa_printf(MSG_DEBUG, + "EAP-pwd: No buffer for reassembly"); + eap_pwd_state(data, FAILURE); + return; + } if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " "attack detected! (%d+%d > %d)", @@ -975,7 +938,7 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * last fragment won't have the M bit set (but we're obviously * buffering fragments so that's how we know it's the last) */ - if (data->in_frag_pos) { + if (data->in_frag_pos && data->inbuf) { pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", @@ -1075,14 +1038,6 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_pwd_register(void) { struct eap_method *eap; - struct timeval tp; - struct timezone tz; - u32 sr; - - sr = 0xdeaddada; - (void) gettimeofday(&tp, &tz); - sr ^= (tp.tv_sec ^ tp.tv_usec); - srandom(sr); eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index 66183f5f5c04..2fc2c0575a94 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -204,7 +204,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, { wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); data->state = FAILURE; - os_free(msg); + wpabuf_free(msg); return NULL; } @@ -340,16 +340,25 @@ static void eap_sake_process_challenge(struct eap_sm *sm, data->state = FAILURE; return; } - eap_sake_derive_keys(sm->user->password, - sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, - data->rand_s, data->rand_p, - (u8 *) &data->tek, data->msk, data->emsk); + if (eap_sake_derive_keys(sm->user->password, + sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, + data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys"); + data->state = FAILURE; + return; + } - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - 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 (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + data->state = FAILURE; + return; + } 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); @@ -382,11 +391,14 @@ static void eap_sake_process_confirm(struct eap_sm *sm, return; } - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - 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 (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p) < 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + return; + } 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); diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 10637d4c66b9..128782735fb3 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -535,6 +535,9 @@ static void eap_sim_process_start(struct eap_sm *sm, goto failed; } + if (data->permanent[0] == EAP_SIM_PERMANENT_PREFIX) + os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi)); + identity_len = sm->identity_len; while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null " diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 8b9e53c61d79..357e72a825f6 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -22,6 +22,7 @@ struct eap_tls_data { enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; u8 eap_type; + int phase2; }; @@ -85,6 +86,8 @@ static void * eap_tls_init(struct eap_sm *sm) data->eap_type = EAP_TYPE_TLS; + data->phase2 = sm->init_phase2; + return data; } @@ -202,6 +205,20 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); eap_tls_state(data, SUCCESS); eap_tls_valid_session(sm, data); + if (sm->serial_num) { + char user[128]; + int user_len; + + user_len = os_snprintf(user, sizeof(user), "cert-%s", + sm->serial_num); + if (eap_user_get(sm, (const u8 *) user, user_len, + data->phase2) < 0) + wpa_printf(MSG_DEBUG, + "EAP-TLS: No user entry found based on the serial number of the client certificate "); + else + wpa_printf(MSG_DEBUG, + "EAP-TLS: Updated user entry based on the serial number of the client certificate "); + } } return res; @@ -288,6 +305,8 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, "EAP-TLS: Resuming previous session"); eap_tls_state(data, SUCCESS); tls_connection_set_success_data_resumed(data->ssl.conn); + /* TODO: Cache serial number with session and update EAP user + * information based on the cached serial number */ } @@ -312,6 +331,7 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) else label = "client EAP encryption"; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -340,6 +360,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) else label = "client EAP encryption"; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 0ae7867fccf7..0eca0ff77409 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -107,7 +107,8 @@ 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, - const char *label, size_t len) + const char *label, const u8 *context, + size_t context_len, size_t len) { u8 *out; @@ -115,8 +116,8 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out, - len)) { + if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, + context, context_len, out, len)) { os_free(out); return NULL; } @@ -146,10 +147,26 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, u8 *out; if (eap_type == EAP_TYPE_TLS && data->tls_v13) { - *len = 64; - return eap_server_tls_derive_key(sm, data, - "EXPORTER_EAP_TLS_Session-Id", - 64); + u8 *id, *method_id; + + /* Session-Id = || Method-Id + * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id", + * "", 64) + */ + *len = 1 + 64; + id = os_malloc(*len); + if (!id) + return NULL; + method_id = eap_server_tls_derive_key( + sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64); + if (!method_id) { + os_free(id); + return NULL; + } + id[0] = eap_type; + os_memcpy(id + 1, method_id, 64); + os_free(method_id); + return id; } if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index b14996b0b990..52bff8afe42d 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -332,7 +332,7 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, struct eap_ttls_data *data, size_t len) { return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", - len); + NULL, 0, len); } @@ -1268,7 +1268,7 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", + "ttls keying material", NULL, 0, EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; @@ -1310,7 +1310,7 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) return NULL; eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "ttls keying material", + "ttls keying material", NULL, 0, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 31f6e72d779a..0b04983adb0e 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -78,7 +78,8 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type); 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, - const char *label, size_t len); + const char *label, const u8 *context, + size_t context_len, 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); diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 9f029b0d3710..a0f27fd2bdb2 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -189,8 +189,9 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) } if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { - eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, - sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, + eloop_ctx, sm) < 0) + sm->timer_tick_enabled = 0; } else { wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); sm->timer_tick_enabled = 0; @@ -204,9 +205,9 @@ static void eapol_enable_timer_tick(struct eapol_sm *sm) if (sm->timer_tick_enabled) return; wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); - sm->timer_tick_enabled = 1; eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0) + sm->timer_tick_enabled = 1; } @@ -2141,8 +2142,8 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) sm->initialize = FALSE; eapol_sm_step(sm); - sm->timer_tick_enabled = 1; - eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0) + sm->timer_tick_enabled = 1; return sm; } diff --git a/src/fst/fst.h b/src/fst/fst.h index 0c0e435b974b..296749120b2a 100644 --- a/src/fst/fst.h +++ b/src/fst/fst.h @@ -19,10 +19,18 @@ #define US_IN_MS 1000 #define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */ -#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) -#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) - -#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) +/* + * These were originally + * #define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US) + * #define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS) + * #define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1) + * but those can overflow 32-bit unsigned integer, so use alternative defines + * to avoid undefined behavior with such overflow. + * LLT_UNIT_US/US_IN_MS = 32/1000 = 4/125 + */ +#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * 125 / 4) +#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * 4 / 125) +#define FST_MAX_LLT_MS (((u32) -1) / 4) #define FST_MAX_PRIO_VALUE ((u8) -1) #define FST_MAX_GROUP_ID_LEN IFNAMSIZ diff --git a/src/lib.rules b/src/lib.rules index 0c79d992a6aa..4ec4711e36ac 100644 --- a/src/lib.rules +++ b/src/lib.rules @@ -6,6 +6,11 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef TEST_FUZZ +CFLAGS += -DCONFIG_NO_RANDOM_POOL +CFLAGS += -DTEST_FUZZ +endif + CFLAGS += -I.. -I../utils diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index b4660c4f9616..157bf891c4ab 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1076,7 +1076,7 @@ static int p2p_run_after_scan(struct p2p_data *p2p) p2p->after_scan_tx->bssid, (u8 *) (p2p->after_scan_tx + 1), p2p->after_scan_tx->len, - p2p->after_scan_tx->wait_time); + p2p->after_scan_tx->wait_time, NULL); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; return 1; @@ -1172,9 +1172,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, u8 seek_count, const char **seek, int freq) { int res; + struct os_reltime start; p2p_dbg(p2p, "Starting find (type=%d)", type); - os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan is already running"); } @@ -1258,6 +1258,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (timeout) eloop_register_timeout(timeout, 0, p2p_find_timeout, p2p, NULL); + os_get_reltime(&start); switch (type) { case P2P_FIND_START_WITH_FULL: if (freq > 0) { @@ -1289,6 +1290,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } + if (!res) + p2p->find_start = start; + 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 */ @@ -1470,7 +1474,8 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) 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->op_channel, + NULL, NULL) == 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 { @@ -2456,7 +2461,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_dbg(p2p, "Probe Req requested Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -4764,9 +4769,12 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { - return p2p_channel_random_social(&p2p->channels, op_class, op_channel); + return p2p_channel_random_social(&p2p->channels, op_class, op_channel, + avoid_list, disallow_list); } @@ -4965,6 +4973,8 @@ 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) { + int res, scheduled; + if (p2p->p2p_scan_running) { p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { @@ -4985,8 +4995,16 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, return 0; } - return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, - buf, len, wait_time); + res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time, &scheduled); + if (res == 0 && scheduled && p2p->in_listen && freq > 0 && + (unsigned int) p2p->drv_in_listen != freq) { + p2p_dbg(p2p, + "Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz", + p2p->drv_in_listen, freq); + p2p_stop_listen_for_freq(p2p, freq); + } + return res; } diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index fac5ce05ae6e..425b037be515 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -103,6 +103,11 @@ struct p2p_go_neg_results { unsigned int vht_center_freq2; + /** + * he - Indicates if IEEE 802.11ax HE is enabled + */ + int he; + /** * ssid - SSID of the group */ @@ -660,6 +665,8 @@ struct p2p_config { * @buf: Frame body (starting from Category field) * @len: Length of buf in octets * @wait_time: How many msec to wait for a response frame + * @scheduled: Return value indicating whether the transmissions was + * scheduled to happen once the radio is available * Returns: 0 on success, -1 on failure * * The Action frame may not be transmitted immediately and the status @@ -670,7 +677,7 @@ struct p2p_config { */ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, - size_t len, unsigned int wait_time); + size_t len, unsigned int wait_time, int *scheduled); /** * send_action_done - Notify that Action frame sequence was completed @@ -2005,6 +2012,8 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * @p2p: P2P config * @op_class: Selected operating class * @op_channel: Selected social channel + * @avoid_list: Channel ranges to try to avoid or %NULL + * @disallow_list: Channel ranges to discard or %NULL * Returns: 0 on success, -1 on failure * * This function is used before p2p_init is called. A random social channel @@ -2012,7 +2021,9 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * returned on success. */ int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, u8 forced); diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 2882c6ad02e7..63eb2e84c376 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -802,7 +802,7 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, wpabuf_put_be16(buf, p2p->cfg->config_methods); } - if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0) return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 16c28a0d95ee..aa18af6c16c6 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -941,7 +941,8 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, group->cfg->interface_addr, group->cfg->interface_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) + wpabuf_head(req), wpabuf_len(req), 200, NULL) + < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 6a4d751c090a..d2c55c9976c2 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -707,7 +707,9 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, 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); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); /* p2p_parse.c */ void p2p_copy_filter_devname(char *dst, size_t dst_len, diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index bbba001a7c93..77d662a47ae2 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -488,7 +488,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, 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->op_channel, NULL, NULL) == 0) { p2p->retry_invite_req = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 2e2aa8ad06f0..1a62a44a2df3 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -413,17 +413,30 @@ int p2p_channel_select(struct p2p_channels *chans, const int *classes, int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { 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)) + /* Try to find available social channels from 2.4 GHz. + * If the avoid_list includes any of the 2.4 GHz social channels, that + * channel is not allowed by p2p_channels_includes() rules. However, it + * is assumed to allow minimal traffic for P2P negotiation, so allow it + * here for social channel selection unless explicitly disallowed in the + * disallow_list. */ + if (p2p_channels_includes(chans, 81, 1) || + (freq_range_list_includes(avoid_list, 2412) && + !freq_range_list_includes(disallow_list, 2412))) chan[num_channels++] = 1; - if (p2p_channels_includes(chans, 81, 6)) + if (p2p_channels_includes(chans, 81, 6) || + (freq_range_list_includes(avoid_list, 2437) && + !freq_range_list_includes(disallow_list, 2437))) chan[num_channels++] = 6; - if (p2p_channels_includes(chans, 81, 11)) + if (p2p_channels_includes(chans, 81, 11) || + (freq_range_list_includes(avoid_list, 2462) && + !freq_range_list_includes(disallow_list, 2462))) chan[num_channels++] = 11; /* Try to find available social channels from 60 GHz */ diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c index 360fcd3f5fcd..1c4dc3e63c9f 100644 --- a/src/pae/ieee802_1x_cp.c +++ b/src/pae/ieee802_1x_cp.c @@ -38,12 +38,10 @@ struct ieee802_1x_cp_sm { /* 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; u64 cipher_suite; Boolean new_sak; /* clear by CP */ @@ -216,6 +214,10 @@ SM_STATE(CP, RECEIVE) SM_ENTRY(CP, RECEIVE); /* RECEIVE state machine not keep with Figure 12-2 in * IEEE Std 802.1X-2010 */ + if (sm->oki) { + ieee802_1x_kay_delete_sas(sm->kay, sm->oki); + os_free(sm->oki); + } sm->oki = sm->lki; sm->oan = sm->lan; sm->otx = sm->ltx; @@ -320,8 +322,11 @@ 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; + if (sm->oki) { + ieee802_1x_kay_delete_sas(sm->kay, sm->oki); + 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, @@ -383,7 +388,8 @@ SM_STEP(CP) if (!sm->elected_self) SM_ENTER(CP, READY); if (sm->elected_self && - (sm->all_receiving || !sm->transmit_when)) + (sm->all_receiving || !sm->controlled_port_enabled || + !sm->transmit_when)) SM_ENTER(CP, TRANSMIT); break; @@ -406,8 +412,8 @@ SM_STEP(CP) case CP_READY: if (sm->new_sak || changed_connect(sm)) - SM_ENTER(CP, RECEIVE); - if (sm->server_transmitting) + SM_ENTER(CP, ABANDON); + if (sm->server_transmitting || !sm->controlled_port_enabled) SM_ENTER(CP, TRANSMIT); break; case CP_ABANDON: @@ -464,7 +470,6 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) 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"); @@ -476,7 +481,6 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) secy_cp_control_confidentiality_offset(sm->kay, sm->confidentiality_offset); - SM_ENTER(CP, INIT); SM_STEP_RUN(CP); return sm; @@ -518,7 +522,6 @@ void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm) eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); os_free(sm->lki); os_free(sm->oki); - os_free(sm->authorization_data); os_free(sm); } @@ -588,19 +591,6 @@ void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean 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 - */ diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h index 695629e5c0bc..a357b278f40a 100644 --- a/src/pae/ieee802_1x_cp.h +++ b/src/pae/ieee802_1x_cp.h @@ -25,7 +25,6 @@ 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, u64 cs); void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset); void ieee802_1x_cp_signal_newsak(void *cp_ctx); diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index cda23fcab41a..b4455c8f4e08 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -1,5 +1,5 @@ /* - * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine + * IEEE 802.1X-2010 Key Agreement Protocol of PAE state machine * Copyright (c) 2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. @@ -27,6 +27,9 @@ #define DEFAULT_ICV_LEN 16 #define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */ +#define MAX_MISSING_SAK_USE 10 /* Accept up to 10 inbound MKPDUs without + * SAK-USE before dropping */ + #define PENDING_PN_EXHAUSTION 0xC0000000 #define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3) @@ -43,7 +46,6 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { .name = CS_NAME_GCM_AES_128, .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, .sak_len = DEFAULT_SA_KEY_LEN, - .index = 0, }, /* GCM-AES-256 */ { @@ -51,7 +53,6 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { .name = CS_NAME_GCM_AES_256, .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, .sak_len = 32, - .index = 1 /* index */ }, }; #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) @@ -61,19 +62,13 @@ static struct mka_alg mka_alg_tbl[] = { { .parameter = MKA_ALGO_AGILITY_2009, - /* 128-bit CAK, KEK, ICK, ICV */ - .cak_len = DEFAULT_ICV_LEN, - .kek_len = DEFAULT_ICV_LEN, - .ick_len = DEFAULT_ICV_LEN, .icv_len = DEFAULT_ICV_LEN, - .cak_trfm = ieee802_1x_cak_128bits_aes_cmac, - .ckn_trfm = ieee802_1x_ckn_128bits_aes_cmac, - .kek_trfm = ieee802_1x_kek_128bits_aes_cmac, - .ick_trfm = ieee802_1x_ick_128bits_aes_cmac, - .icv_hash = ieee802_1x_icv_128bits_aes_cmac, - - .index = 1, + .cak_trfm = ieee802_1x_cak_aes_cmac, + .ckn_trfm = ieee802_1x_ckn_aes_cmac, + .kek_trfm = ieee802_1x_kek_aes_cmac, + .ick_trfm = ieee802_1x_ick_aes_cmac, + .icv_hash = ieee802_1x_icv_aes_cmac, }, }; #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl)) @@ -109,6 +104,34 @@ static u8 get_mka_param_body_type(const void *body) } +static const char * mi_txt(const u8 *mi) +{ + static char txt[MI_LEN * 2 + 1]; + + wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN); + return txt; +} + + +static const char * sci_txt(const struct ieee802_1x_mka_sci *sci) +{ + static char txt[ETH_ALEN * 3 + 1 + 5 + 1]; + + os_snprintf(txt, sizeof(txt), MACSTR "@%u", + MAC2STR(sci->addr), be_to_host16(sci->port)); + return txt; +} + + +static const char * algo_agility_txt(const u8 *algo_agility) +{ + static char txt[4 * 2 + 1]; + + wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4); + return txt; +} + + /** * ieee802_1x_mka_dump_basic_body - */ @@ -120,26 +143,25 @@ ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body) if (!body) return; + /* IEEE Std 802.1X-2010, Figure 11-8 */ 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_capability); - wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", 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", + wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set"); + wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version); + wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority); + wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server); + wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired); + wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d", + body->macsec_capability); + wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len); + wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci)); + wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s", + mi_txt(body->actor_mi)); + wpa_printf(MSG_DEBUG, "\tActor's Message 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)); + wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s", + algo_agility_txt(body->algo_agility)); + wpa_hexdump(MSG_DEBUG, "\tCAK Name", body->ckn, + body_len + MKA_HDR_LEN - sizeof(*body)); } @@ -157,20 +179,21 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) if (body == NULL) return; + /* IEEE Std 802.1X-2010, Figure 11-9 */ 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...: %zu", body_len); + wpa_printf(MSG_DEBUG, "Live Peer List parameter set"); + wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len); } else if (body->type == MKA_POTENTIAL_PEER_LIST) { - wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***"); - wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); + wpa_printf(MSG_DEBUG, "Potential Peer List parameter set"); + wpa_printf(MSG_DEBUG, "\tBody Length: %zu", 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)); + wpa_printf(MSG_DEBUG, "\tMember Id: %s Message Number: %d", + mi_txt(mi), be_to_host32(mn)); } } @@ -186,18 +209,20 @@ ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body) if (body == NULL) return; + /* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */ 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", + wpa_printf(MSG_DEBUG, "Distributed SAK parameter set"); + wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan); + wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d", body->confid_offset); - wpa_printf(MSG_INFO, "\tBody Length...........: %zu", body_len); + wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len); if (!body_len) return; - wpa_printf(MSG_INFO, "\tKey Number............: %d", + wpa_printf(MSG_DEBUG, "\tKey Number............: %d", be_to_host32(body->kn)); - wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24); + /* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */ + wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24); } @@ -218,33 +243,32 @@ ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body) if (body == NULL) return; + /* IEEE Std 802.1X-2010, Figure 11-10 */ body_len = get_mka_param_body_len(body); - wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***"); + wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set"); 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, "\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 Tx.........: %s", yes_no(body->ptx)); + wpa_printf(MSG_DEBUG, "\tPlain 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 Server MI....: %s", mi_txt(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", + wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi)); + wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u", be_to_host32(body->okn)); - wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u", + wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u", be_to_host32(body->olpn)); } @@ -371,7 +395,7 @@ ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, */ static struct macsec_ciphersuite * ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, - const u8 *cs_id) + const u8 *cs_id, unsigned int *idx) { unsigned int i; u64 cs; @@ -381,8 +405,10 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, cs = be_to_host64(_cs); for (i = 0; i < CS_TABLE_SIZE; i++) { - if (cipher_suite_tbl[i].id == cs) + if (cipher_suite_tbl[i].id == cs) { + *idx = i; return &cipher_suite_tbl[i]; + } } return NULL; @@ -464,7 +490,7 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC", + "KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC", an, lowest_pn); return psa; @@ -511,8 +537,8 @@ ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci) psc->receiving = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create receive SC"); - wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci)); + wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s", + sci_txt(&psc->sci)); return psc; } @@ -549,10 +575,8 @@ ieee802_1x_kay_deinit_receive_sc( static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *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); + wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s", + mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci)); } @@ -571,6 +595,7 @@ ieee802_1x_kay_create_peer(const u8 *mi, u32 mn) peer->mn = mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; peer->sak_used = FALSE; + peer->missing_sak_use_count = 0; return peer; } @@ -599,9 +624,13 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, return NULL; } + if (secy_create_receive_sc(participant->kay, rxsc)) { + os_free(rxsc); + os_free(peer); + return NULL; + } dl_list_add(&participant->live_peers, &peer->list); dl_list_add(&participant->rxsc_list, &rxsc->list); - secy_create_receive_sc(participant->kay, rxsc); wpa_printf(MSG_DEBUG, "KaY: Live peer created"); ieee802_1x_kay_dump_peer(peer); @@ -625,7 +654,7 @@ ieee802_1x_kay_create_potential_peer( dl_list_add(&participant->potential_peers, &peer->list); - wpa_printf(MSG_DEBUG, "KaY: potential peer created"); + wpa_printf(MSG_DEBUG, "KaY: Potential peer created"); ieee802_1x_kay_dump_peer(peer); return peer; @@ -655,14 +684,19 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, peer->mn = mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; - wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer"); + wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer"); ieee802_1x_kay_dump_peer(peer); dl_list_del(&peer->list); + if (secy_create_receive_sc(participant->kay, rxsc)) { + wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer"); + os_free(rxsc); + os_free(peer); + return NULL; + } dl_list_add_tail(&participant->live_peers, &peer->list); dl_list_add(&participant->rxsc_list, &rxsc->list); - secy_create_receive_sc(participant->kay, rxsc); return peer; } @@ -704,12 +738,15 @@ ieee802_1x_mka_encode_basic_body( { struct ieee802_1x_mka_basic_body *body; struct ieee802_1x_kay *kay = participant->kay; - unsigned int length = ieee802_1x_mka_basic_body_length(participant); + unsigned int length = sizeof(struct ieee802_1x_mka_basic_body); - body = wpabuf_put(buf, length); + length += participant->ckn.len; + body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length)); body->version = kay->mka_version; body->priority = kay->actor_priority; + /* The Key Server flag is set if and only if the participant has not + * decided that another participant is or will be the Key Server. */ if (participant->is_elected) body->key_server = participant->is_key_server; else @@ -765,11 +802,11 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, if (body->version > MKA_VERSION_ID) { wpa_printf(MSG_DEBUG, - "KaY: peer's version(%d) greater than mka current version(%d)", + "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"); + wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server"); return NULL; } @@ -783,7 +820,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { - wpa_printf(MSG_DEBUG, "Peer is not included in my CA"); + wpa_printf(MSG_DEBUG, + "KaY: Peer is not included in my CA - ignore MKPDU"); return NULL; } @@ -791,6 +829,9 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { if (!reset_participant_mi(participant)) return NULL; + wpa_printf(MSG_DEBUG, + "KaY: Peer using my MI - selected a new random MI: %s", + mi_txt(participant->mi)); } os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN); @@ -802,24 +843,48 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, /* 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? + /* Check duplicated SCI + * + * A duplicated SCI indicates either an active attacker or + * a valid peer whose MI is being changed. The latter scenario + * is more likely because to have gotten this far the received + * MKPDU must have had a valid ICV, indicating the peer holds + * the same CAK as our participant. + * + * Before creating a new peer object for the new MI we must + * clean up the resources (SCs and SAs) associated with the + * old peer. An easy way to do this is to ignore MKPDUs with + * the new MI's for now and just wait for the old peer to + * time out and clean itself up (within MKA_LIFE_TIME). + * + * This method is preferable to deleting the old peer here + * and now and continuing on with processing because if this + * MKPDU is from an attacker it's better to ignore the MKPDU + * than to process it (and delete a valid peer as well). */ peer = ieee802_1x_kay_get_peer_sci(participant, &body->actor_sci); if (peer) { + time_t new_expire; + wpa_printf(MSG_WARNING, - "KaY: duplicated SCI detected, Maybe active attacker"); - dl_list_del(&peer->list); - os_free(peer); + "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU"); + /* Reduce timeout to speed up this process but left the + * chance for old one to prove aliveness. */ + new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000; + if (peer->expire > new_expire) + peer->expire = new_expire; + return NULL; } peer = ieee802_1x_kay_create_potential_peer( participant, body->actor_mi, be_to_host32(body->actor_mn)); - if (!peer) + if (!peer) { + wpa_printf(MSG_DEBUG, + "KaY: No potential peer entry found - ignore MKPDU"); return NULL; + } peer->macsec_desired = body->macsec_desired; peer->macsec_capability = body->macsec_capability; @@ -827,13 +892,13 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, 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_capability = body->macsec_capability; 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"); + wpa_printf(MSG_WARNING, + "KaY: The peer MN did not increase - ignore MKPDU"); return NULL; } @@ -978,8 +1043,8 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, for (pos = mka_msg, left_len = msg_len; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; - left_len -= body_len + MKA_HDR_LEN, - pos += body_len + MKA_HDR_LEN) { + left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN, + pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) { hdr = (struct ieee802_1x_mka_hdr *) pos; body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); @@ -1014,9 +1079,15 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, peer_mi = (const struct ieee802_1x_mka_peer_id *) (pos + MKA_HDR_LEN + i); if (os_memcmp(peer_mi->mi, participant->mi, - MI_LEN) == 0 && - be_to_host32(peer_mi->mn) == participant->mn) - return TRUE; + MI_LEN) == 0) { + u32 mn = be_to_host32(peer_mi->mn); + + wpa_printf(MSG_DEBUG, + "KaY: My MI - received MN %u, most recently transmitted MN %u", + mn, participant->mn); + if (mn == participant->mn) + return TRUE; + } } } @@ -1072,7 +1143,6 @@ static int ieee802_1x_mka_decode_live_peer_body( peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi); if (peer) { peer->mn = peer_mn; - peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; } else if (!ieee802_1x_kay_create_potential_peer( participant, peer_mi->mi, peer_mn)) { return -1; @@ -1154,27 +1224,38 @@ ieee802_1x_mka_get_sak_use_length( /** - * + * ieee802_1x_mka_get_lpn */ 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; + struct transmit_sa *txsa; 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); + dl_list_for_each(txsa, &principal->txsc->sa_list, + struct transmit_sa, list) { + if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { + /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses + * MKA to communicate the lowest PN used for + * transmission with the SAK within the last two + * seconds". Achieve this 2 second delay by setting the + * lpn using the transmit next PN (i.e., txsa->next_pn) + * that was read last time here (i.e., mka_hello_time + * 2 seconds ago). + * + * The lowest acceptable PN is the same as the last + * transmitted PN, which is one less than the next + * transmit PN. + * + * NOTE: This method only works if mka_hello_time is 2s. + */ + lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0; - lpn = lpn > rxsa->lowest_pn ? - lpn : rxsa->lowest_pn; - break; - } + /* Now read the current transmit next PN for use next + * time through. */ + secy_get_transmit_next_pn(principal->kay, txsa); + break; } } @@ -1214,8 +1295,9 @@ ieee802_1x_mka_encode_sak_use_body( return 0; } - /* data protect, lowest accept packet number */ - body->delay_protect = kay->macsec_replay_protect; + /* data delay protect */ + body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME; + /* lowest accept packet number */ pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); if (pn > kay->pn_exhaustion) { wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); @@ -1276,7 +1358,8 @@ ieee802_1x_mka_decode_sak_use_body( 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 receive_sc *rxsc; + struct receive_sa *rxsa; struct data_key *sa_key = NULL; size_t body_len; struct ieee802_1x_mka_ki ki; @@ -1292,7 +1375,9 @@ ieee802_1x_mka_decode_sak_use_body( 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"); + wpa_printf(MSG_WARNING, + "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set", + mi_txt(participant->current_peer_id.mi)); return -1; } @@ -1336,7 +1421,7 @@ ieee802_1x_mka_decode_sak_use_body( } } if (!found) { - wpa_printf(MSG_WARNING, "KaY: Latest key is invalid"); + wpa_printf(MSG_INFO, "KaY: Latest key is invalid"); return -1; } if (os_memcmp(participant->lki.mi, body->lsrv_mi, @@ -1366,7 +1451,7 @@ ieee802_1x_mka_decode_sak_use_body( if (body->delay_protect && (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) { wpa_printf(MSG_WARNING, - "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE"); + "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE"); return -1; } @@ -1385,7 +1470,7 @@ ieee802_1x_mka_decode_sak_use_body( ieee802_1x_cp_sm_step(kay->cp); } - /* if i'm key server, and detects peer member pn exhaustion, rekey.*/ + /* if I'm key server, and detects peer member pn exhaustion, rekey. */ lpn = be_to_host32(body->llpn); if (lpn > kay->pn_exhaustion) { if (participant->is_key_server) { @@ -1394,26 +1479,41 @@ ieee802_1x_mka_decode_sak_use_body( } } + if (sa_key) + sa_key->next_pn = lpn; found = FALSE; - dl_list_for_each(txsa, &participant->txsc->sa_list, - struct transmit_sa, list) { - if (sa_key != NULL && txsa->pkey == sa_key) { - found = TRUE; - break; + dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc, + list) { + dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, + list) { + if (sa_key && rxsa->pkey == sa_key) { + found = TRUE; + break; + } } + if (found) + break; } if (!found) { - wpa_printf(MSG_WARNING, "KaY: Can't find txsa"); + wpa_printf(MSG_WARNING, "KaY: Can't find rxsa"); 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(kay, txsa); - if (lpn > txsa->next_pn) { - secy_set_transmit_next_pn(kay, txsa); - wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn); + if (body->delay_protect) { + secy_get_receive_lowest_pn(participant->kay, rxsa); + if (lpn > rxsa->lowest_pn) { + /* Delay protect window (communicated via MKA) is + * tighter than SecY's current replay protect window, + * so tell SecY the new (and higher) lpn. */ + rxsa->lowest_pn = lpn; + secy_set_receive_lowest_pn(participant->kay, rxsa); + wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn); + } + /* FIX: Delay protection for olpn not implemented. + * Note that Old Key is only active for MKA_SAK_RETIRE_TIME + * (3 seconds) and delay protection does allow PN's within + * a 2 seconds window, so olpn would be a lot of work for + * just 1 second's worth of protection. */ } return 0; @@ -1427,7 +1527,8 @@ static Boolean ieee802_1x_mka_dist_sak_body_present( struct ieee802_1x_mka_participant *participant) { - return participant->to_dist_sak && participant->new_key; + return participant->is_key_server && participant->to_dist_sak && + participant->new_key; } @@ -1478,6 +1579,11 @@ ieee802_1x_mka_encode_dist_sak_body( } sak = participant->new_key; + if (!sak) { + wpa_printf(MSG_DEBUG, + "KaY: No SAK available to build Distributed SAK parameter set"); + return -1; + } body->confid_offset = sak->confidentiality_offset; body->dan = sak->an; body->kn = host_to_be32(sak->key_identifier.kn); @@ -1492,7 +1598,7 @@ ieee802_1x_mka_encode_dist_sak_body( os_memcpy(body->sak, &cs, CS_ID_LEN); sak_pos = CS_ID_LEN; } - if (aes_wrap(participant->kek.key, 16, + if (aes_wrap(participant->kek.key, participant->kek.len, cipher_suite_tbl[cs_index].sak_len / 8, sak->key, body->sak + sak_pos)) { wpa_printf(MSG_ERROR, "KaY: AES wrap failed"); @@ -1514,6 +1620,7 @@ static void ieee802_1x_kay_init_data_key(struct data_key *pkey) pkey->receives = TRUE; os_get_time(&pkey->created_time); + pkey->next_pn = 1; pkey->user = 1; } @@ -1553,7 +1660,7 @@ ieee802_1x_mka_decode_dist_sak_body( } if (participant->is_key_server) { wpa_printf(MSG_ERROR, - "KaY: I can't accept the distributed SAK as myself is key server "); + "KaY: Reject distributed SAK since I'm a key server"); return -1; } if (!kay->macsec_desired || @@ -1582,7 +1689,7 @@ ieee802_1x_mka_decode_dist_sak_body( participant->advised_desired = FALSE; ieee802_1x_cp_connect_authenticated(kay->cp); ieee802_1x_cp_sm_step(kay->cp); - wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); + wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec"); participant->to_use_sak = FALSE; return 0; } @@ -1601,7 +1708,8 @@ ieee802_1x_mka_decode_dist_sak_body( 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"); + wpa_printf(MSG_DEBUG, + "KaY: SAK has already been installed - do not set it again"); return 0; } } @@ -1612,7 +1720,10 @@ ieee802_1x_mka_decode_dist_sak_body( kay->macsec_csindex = DEFAULT_CS_INDEX; cs = &cipher_suite_tbl[kay->macsec_csindex]; } else { - cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak); + unsigned int idx; + + cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak, + &idx); if (!cs) { wpa_printf(MSG_ERROR, "KaY: I can't support the Cipher Suite advised by key server"); @@ -1620,7 +1731,7 @@ ieee802_1x_mka_decode_dist_sak_body( } sak_len = cs->sak_len; wrap_sak = body->sak + CS_ID_LEN; - kay->macsec_csindex = cs->index; + kay->macsec_csindex = idx; } unwrap_sak = os_zalloc(sak_len); @@ -1628,13 +1739,13 @@ ieee802_1x_mka_decode_dist_sak_body( 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)) { + if (aes_unwrap(participant->kek.key, participant->kek.len, + sak_len >> 3, wrap_sak, unwrap_sak)) { wpa_printf(MSG_ERROR, "KaY: AES unwrap failed"); os_free(unwrap_sak); return -1; } - wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:", + wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:", unwrap_sak, sak_len); sa_key = os_zalloc(sizeof(*sa_key)); @@ -1691,7 +1802,12 @@ ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) { int length; - length = sizeof(struct ieee802_1x_mka_icv_body); + /* Determine if we need space for the ICV Indicator */ + if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != + DEFAULT_ICV_LEN) + length = sizeof(struct ieee802_1x_mka_icv_body); + else + length = 0; length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; return MKA_ALIGN_LENGTH(length); @@ -1710,20 +1826,23 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, u8 cmac[MAX_ICV_LEN]; length = ieee802_1x_mka_get_icv_length(participant); - if (length != DEFAULT_ICV_LEN) { + if (mka_alg_tbl[participant->kay->mka_algindex].icv_len != + DEFAULT_ICV_LEN) { + wpa_printf(MSG_DEBUG, "KaY: ICV Indicator"); body = wpabuf_put(buf, MKA_HDR_LEN); body->type = MKA_ICV_INDICATOR; - set_mka_param_body_len(body, length - MKA_HDR_LEN); + length -= MKA_HDR_LEN; + set_mka_param_body_len(body, length); } 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"); + participant->ick.key, participant->ick.len, + wpabuf_head(buf), wpabuf_len(buf), cmac)) { + wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV"); return -1; } + wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length); - if (length != DEFAULT_ICV_LEN) - length -= MKA_HDR_LEN; os_memcpy(wpabuf_put(buf, length), cmac, length); return 0; @@ -1732,12 +1851,12 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, /** * ieee802_1x_mka_decode_icv_body - */ -static u8 * +static const 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; + const struct ieee802_1x_mka_hdr *hdr; + const struct ieee802_1x_mka_icv_body *body; size_t body_len; size_t left_len; u8 body_type; @@ -1745,12 +1864,12 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, 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); + while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { + hdr = (const struct ieee802_1x_mka_hdr *) pos; + body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); body_type = get_mka_param_body_type(hdr); - if (left_len < (body_len + MKA_HDR_LEN)) + if (left_len < body_len + MKA_HDR_LEN) break; if (body_type != MKA_ICV_INDICATOR) { @@ -1759,16 +1878,15 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, continue; } - body = (struct ieee802_1x_mka_icv_body *)pos; + body = (const struct ieee802_1x_mka_icv_body *) pos; if (body_len - < mka_alg_tbl[participant->kay->mka_algindex].icv_len) { + < mka_alg_tbl[participant->kay->mka_algindex].icv_len) return NULL; - } return body->icv; } - return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN); + return mka_msg + msg_len - DEFAULT_ICV_LEN; } @@ -1787,7 +1905,7 @@ ieee802_1x_mka_decode_dist_cak_body( body_len = get_mka_param_body_len(hdr); if (body_len < 28) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets", + "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets", body_len); return -1; } @@ -1811,7 +1929,7 @@ ieee802_1x_mka_decode_kmd_body( body_len = get_mka_param_body_len(hdr); if (body_len < 5) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets", + "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets", body_len); return -1; } @@ -1842,7 +1960,7 @@ struct mka_param_body_handler { static struct mka_param_body_handler mka_body_handler[] = { - /* basic parameter set */ + /* Basic parameter set */ { .body_tx = ieee802_1x_mka_encode_basic_body, .body_rx = NULL, @@ -1850,7 +1968,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_basic_body_present }, - /* live peer list parameter set */ + /* Live Peer List parameter set */ { .body_tx = ieee802_1x_mka_encode_live_peer_body, .body_rx = ieee802_1x_mka_decode_live_peer_body, @@ -1858,7 +1976,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_live_peer_body_present }, - /* potential peer list parameter set */ + /* Potential Peer List parameter set */ { .body_tx = ieee802_1x_mka_encode_potential_peer_body, .body_rx = ieee802_1x_mka_decode_potential_peer_body, @@ -1866,7 +1984,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_potential_peer_body_present }, - /* sak use parameter set */ + /* MACsec SAK Use parameter set */ { .body_tx = ieee802_1x_mka_encode_sak_use_body, .body_rx = ieee802_1x_mka_decode_sak_use_body, @@ -1874,7 +1992,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_sak_use_body_present }, - /* distribute sak parameter set */ + /* Distributed SAK parameter set */ { .body_tx = ieee802_1x_mka_encode_dist_sak_body, .body_rx = ieee802_1x_mka_decode_dist_sak_body, @@ -1882,7 +2000,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = ieee802_1x_mka_dist_sak_body_present }, - /* distribute cak parameter set */ + /* Distribute CAK parameter set */ { .body_tx = NULL, .body_rx = ieee802_1x_mka_decode_dist_cak_body, @@ -1890,7 +2008,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = NULL }, - /* kmd parameter set */ + /* KMD parameter set */ { .body_tx = NULL, .body_rx = ieee802_1x_mka_decode_kmd_body, @@ -1898,7 +2016,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = NULL }, - /* announce parameter set */ + /* Announcement parameter set */ { .body_tx = NULL, .body_rx = ieee802_1x_mka_decode_announce_body, @@ -1906,7 +2024,7 @@ static struct mka_param_body_handler mka_body_handler[] = { .body_present = NULL }, - /* icv parameter set */ + /* ICV Indicator parameter set */ { .body_tx = ieee802_1x_mka_encode_icv_body, .body_rx = NULL, @@ -1965,7 +2083,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) */ if (dl_list_empty(&participant->live_peers)) { wpa_printf(MSG_ERROR, - "KaY: Live peers list must not empty when generating fresh SAK"); + "KaY: Live peers list must not be empty when generating fresh SAK"); return -1; } @@ -1979,7 +2097,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) */ if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) { wpa_printf(MSG_ERROR, - "KaY: Life time have not elapsed since prior SAK distributed"); + "KaY: Life time has not elapsed since prior SAK distributed"); return -1; } @@ -2016,14 +2134,17 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) ctx_offset += sizeof(participant->mi); os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn)); - if (key_len == 16) { - ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, key); - } else if (key_len == 32) { - ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, key); + if (key_len == 16 || key_len == 32) { + if (ieee802_1x_sak_aes_cmac(participant->cak.key, + participant->cak.len, + context, ctx_len, + key, key_len)) { + wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK"); + goto fail; + } } else { - wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); + wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported", + key_len); goto fail; } wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len); @@ -2155,7 +2276,7 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) participant->is_key_server = TRUE; participant->principal = TRUE; participant->new_sak = TRUE; - wpa_printf(MSG_DEBUG, "KaY: I is elected as key server"); + wpa_printf(MSG_DEBUG, "KaY: I am elected as key server"); participant->to_dist_sak = FALSE; participant->is_elected = TRUE; @@ -2163,6 +2284,9 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) sizeof(kay->key_server_sci)); kay->key_server_priority = kay->actor_priority; } else if (key_server) { + wpa_printf(MSG_DEBUG, + "KaY: Peer %s was elected as the key server", + mi_txt(key_server->mi)); ieee802_1x_cp_set_electedself(kay->cp, FALSE); if (!sci_equal(&kay->key_server_sci, &key_server->sci)) { ieee802_1x_cp_signal_chgdserver(kay->cp); @@ -2281,11 +2405,19 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr, sizeof(ether_hdr->dest)); ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL); + wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR + " Ethertype=0x%x", + MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src), + be_to_host16(ether_hdr->ethertype)); 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); + eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf)); + wpa_printf(MSG_DEBUG, + "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", + eapol_hdr->version, eapol_hdr->type, + be_to_host16(eapol_hdr->length)); for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { if (mka_body_handler[i].body_present && @@ -2298,6 +2430,7 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, return 0; } + /** * ieee802_1x_participant_send_mkpdu - */ @@ -2310,7 +2443,8 @@ ieee802_1x_participant_send_mkpdu( size_t length = 0; unsigned int i; - wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU"); + wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)", + kay->if_name); length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { if (mka_body_handler[i].body_present && @@ -2325,10 +2459,11 @@ ieee802_1x_participant_send_mkpdu( } if (ieee802_1x_kay_encode_mkpdu(participant, buf)) { - wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!"); + wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail"); return -1; } + wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf); l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf)); wpabuf_free(buf); @@ -2365,6 +2500,8 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant = (struct ieee802_1x_mka_participant *)eloop_ctx; kay = participant->kay; + wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)", + kay->if_name); if (participant->cak_life) { if (now > participant->cak_life) goto delete_mka; @@ -2452,7 +2589,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) } } - if (participant->new_sak) { + if (participant->new_sak && participant->is_key_server) { if (!ieee802_1x_kay_generate_new_sak(participant)) participant->to_dist_sak = TRUE; @@ -2465,7 +2602,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant->retry_count++; } - eloop_register_timeout(MKA_HELLO_TIME / 1000, 0, + eloop_register_timeout(kay->mka_hello_time / 1000, 0, ieee802_1x_participant_timer, participant, NULL); @@ -2514,7 +2651,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC", + "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC", an, next_PN); return psa; @@ -2557,8 +2694,8 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci) psc->enciphering_sa = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create transmit SC"); - wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci)); + wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s", + sci_txt(&psc->sci)); return psc; } @@ -2709,7 +2846,7 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, } } if (!latest_sak) { - wpa_printf(MSG_ERROR, "lki related sak not found"); + wpa_printf(MSG_ERROR, "KaY: lki related sak not found"); return -1; } @@ -2730,7 +2867,9 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, ieee802_1x_delete_transmit_sa(kay, txsa); txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, - 1, latest_sak); + latest_sak->next_pn ? + latest_sak->next_pn : 1, + latest_sak); if (!txsa) return -1; @@ -2779,12 +2918,12 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, 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)) { + if (principal->new_key == sa_key) + principal->new_key = NULL; dl_list_del(&sa_key->list); ieee802_1x_kay_deinit_data_key(sa_key); break; } - if (principal->new_key == sa_key) - principal->new_key = NULL; } return 0; @@ -2872,7 +3011,8 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) /** * ieee802_1x_kay_mkpdu_sanity_check - - * sanity check specified in clause 11.11.2 of IEEE802.1X-2010 + * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of + * MKPDUs) */ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, const u8 *buf, size_t len) @@ -2886,34 +3026,49 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, size_t body_len; size_t ckn_len; u8 icv[MAX_ICV_LEN]; - u8 *msg_icv; + const u8 *msg_icv; + /* len > eth+eapol header already verified in kay_l2_receive(); + * likewise, eapol_hdr->length validated there */ 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 */ + wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR + " Ethertype=0x%x", + MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src), + be_to_host16(eth_hdr->ethertype)); + + /* the destination address shall not be an individual address */ if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) { - wpa_printf(MSG_MSGDUMP, + wpa_printf(MSG_DEBUG, "KaY: ethernet destination address is not PAE group address"); return -1; } - /* MKPDU should not be less than 32 octets */ + wpa_printf(MSG_DEBUG, + "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u", + eapol_hdr->version, eapol_hdr->type, + be_to_host16(eapol_hdr->length)); + + /* MKPDU shall not be 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"); + wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets"); return -1; } - /* MKPDU should be a multiple of 4 octets */ + /* MKPDU shall be a multiple of 4 octets */ if ((mka_msg_len % 4) != 0) { - wpa_printf(MSG_MSGDUMP, + wpa_printf(MSG_DEBUG, "KaY: MKPDU is not multiple of 4 octets"); return -1; } + wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)", + mka_hdr, mka_msg_len); + + /* Room for body_len already verified in kay_l2_receive() */ 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) { @@ -2932,24 +3087,27 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, ckn_len = body_len - (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) { - wpa_printf(MSG_ERROR, + wpa_printf(MSG_WARNING, "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)", ckn_len, MAX_CKN_LEN); return -1; } + ieee802_1x_mka_dump_basic_body(body); + /* CKN should be owned by I */ participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { - wpa_printf(MSG_DEBUG, "CKN is not included in my CA"); + wpa_printf(MSG_DEBUG, "KaY: 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"); + wpa_printf(MSG_INFO, + "KaY: Peer's algorithm agility (%s) not supported", + algo_agility_txt(body->algo_agility)); return -1; } @@ -2959,23 +3117,29 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, * 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, + if (len < mka_alg_tbl[kay->mka_algindex].icv_len || + mka_alg_tbl[kay->mka_algindex].icv_hash( + participant->ick.key, participant->ick.len, buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) { - wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed"); + wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV"); return -1; } - msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr, + msg_icv = ieee802_1x_mka_decode_icv_body(participant, + (const u8 *) mka_hdr, mka_msg_len); if (!msg_icv) { - wpa_printf(MSG_ERROR, "KaY: No ICV"); + wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it"); return -1; } + wpa_hexdump(MSG_DEBUG, "KaY: Received ICV", + msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len); if (os_memcmp_const(msg_icv, icv, mka_alg_tbl[kay->mka_algindex].icv_len) != 0) { - wpa_printf(MSG_ERROR, + wpa_printf(MSG_WARNING, "KaY: Computed ICV is not equal to Received ICV"); + wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV", + icv, mka_alg_tbl[kay->mka_algindex].icv_len); return -1; } @@ -2991,13 +3155,19 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, { struct ieee802_1x_mka_participant *participant; struct ieee802_1x_mka_hdr *hdr; + struct ieee802_1x_kay_peer *peer; size_t body_len; size_t left_len; u8 body_type; int i; const u8 *pos; Boolean handled[256]; + Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use + * parameter set */ + Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer; + wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)", + kay->if_name); if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) return -1; @@ -3011,17 +3181,24 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, /* to skip basic parameter set */ hdr = (struct ieee802_1x_mka_hdr *) pos; - body_len = get_mka_param_body_len(hdr); + body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); + if (left_len < body_len + MKA_HDR_LEN) + return -1; pos += body_len + MKA_HDR_LEN; left_len -= body_len + MKA_HDR_LEN; /* check i am in the peer's peer list */ - if (ieee802_1x_mka_i_in_peerlist(participant, pos, left_len) && - !ieee802_1x_kay_is_in_live_peer(participant, - participant->current_peer_id.mi)) { + i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos, + left_len); + is_in_live_peer = ieee802_1x_kay_is_in_live_peer( + participant, participant->current_peer_id.mi); + wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s", + yes_no(i_in_peerlist), yes_no(is_in_live_peer)); + if (i_in_peerlist && !is_in_live_peer) { /* accept the peer as live peer */ - if (ieee802_1x_kay_is_in_potential_peer( - participant, participant->current_peer_id.mi)) { + is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer( + participant, participant->current_peer_id.mi); + if (is_in_potential_peer) { if (!ieee802_1x_kay_move_live_peer( participant, participant->current_peer_id.mi, @@ -3051,7 +3228,7 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, pos += body_len + MKA_HDR_LEN, left_len -= body_len + MKA_HDR_LEN) { hdr = (struct ieee802_1x_mka_hdr *) pos; - body_len = get_mka_param_body_len(hdr); + body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr)); body_type = get_mka_param_body_type(hdr); if (body_type == MKA_ICV_INDICATOR) @@ -3065,21 +3242,103 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, return -1; } - if (handled[body_type]) + if (handled[body_type]) { + wpa_printf(MSG_DEBUG, + "KaY: Ignore duplicated body type %u", + body_type); continue; + } handled[body_type] = TRUE; if (body_type < ARRAY_SIZE(mka_body_handler) && mka_body_handler[body_type].body_rx) { - mka_body_handler[body_type].body_rx - (participant, pos, left_len); + if (mka_body_handler[body_type].body_rx + (participant, pos, left_len) != 0) { + /* Handle parameter set failure */ + if (body_type != MKA_SAK_USE) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", + body_type); + return -1; + } + + /* Ideally DIST-SAK should be processed before + * SAK-USE. Unfortunately IEEE Std 802.1X-2010, + * 11.11.3 (Encoding MKPDUs) states SAK-USE(3) + * must always be encoded before DIST-SAK(4). + * Rather than redesigning mka_body_handler so + * that it somehow processes DIST-SAK before + * SAK-USE, just ignore SAK-USE failures if + * DIST-SAK is also present in this MKPDU. */ + bad_sak_use = TRUE; + } } else { wpa_printf(MSG_ERROR, - "The type %d is not supported in this MKA version %d", + "KaY: The body type %d is not supported in this MKA version %d", body_type, MKA_VERSION_ID); } } + if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed", + MKA_SAK_USE); + if (!reset_participant_mi(participant)) + wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); + else + wpa_printf(MSG_DEBUG, + "KaY: Selected a new random MI: %s", + mi_txt(participant->mi)); + return -1; + } + + /* Detect missing parameter sets */ + peer = ieee802_1x_kay_get_live_peer(participant, + participant->current_peer_id.mi); + if (peer) { + /* MKPDU is from live peer */ + if (!handled[MKA_SAK_USE]) { + /* Once a live peer starts sending SAK-USE, it should be + * sent every time. */ + if (peer->sak_used) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE"); + return -1; + } + + /* Live peer is probably hung if it hasn't sent SAK-USE + * after a reasonable number of MKPDUs. Drop the MKPDU, + * which will eventually force an timeout. */ + if (++peer->missing_sak_use_count > + MAX_MISSING_SAK_USE) { + wpa_printf(MSG_INFO, + "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE"); + return -1; + } + } else { + peer->missing_sak_use_count = 0; + + /* Only update live peer watchdog after successful + * decode of all parameter sets */ + peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; + } + } else { + /* MKPDU is from new or potential peer */ + peer = ieee802_1x_kay_get_peer(participant, + participant->current_peer_id.mi); + if (!peer) { + wpa_printf(MSG_DEBUG, "KaY: No peer entry found"); + return -1; + } + + /* Do not update potential peer watchdog. Per IEEE Std + * 802.1X-2010, 9.4.3, potential peers need to show liveness by + * including our MI/MN in their transmitted MKPDU (within + * potential or live parameter sets). Whena potential peer does + * include our MI/MN in an MKPDU, we respond by moving the peer + * from 'potential_peers' to 'live_peers'. */ + } + kay->active = TRUE; participant->retry_count = 0; participant->active = TRUE; @@ -3095,6 +3354,9 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, struct ieee802_1x_kay *kay = ctx; struct ieee8023_hdr *eth_hdr; struct ieee802_1x_hdr *eapol_hdr; + size_t calc_len; + + /* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */ /* must contain at least ieee8023_hdr + ieee802_1x_hdr */ if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) { @@ -3105,13 +3367,21 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, eth_hdr = (struct ieee8023_hdr *) buf; eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1); - if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) + - be_to_host16(eapol_hdr->length)) { - wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)", + calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) + + be_to_host16(eapol_hdr->length); + if (len < calc_len) { + wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)", (unsigned long) len, - (unsigned long) be_to_host16(eapol_hdr->length)); + (unsigned long) calc_len, + be_to_host16(eapol_hdr->length)); return; } + if (len > calc_len) { + wpa_hexdump(MSG_DEBUG, + "KaY: Ignore extra octets following the Packey Body field", + &buf[calc_len], len - calc_len); + len = calc_len; + } if (eapol_hdr->version < EAPOL_VERSION) { wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA", @@ -3120,11 +3390,12 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, } if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE || eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) - return; + return; /* ignore other EAPOL types silently here */ - wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len); + wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len); if (dl_list_empty(&kay->participant_list)) { - wpa_printf(MSG_ERROR, "KaY: no MKA participant instance"); + wpa_printf(MSG_ERROR, + "KaY: No MKA participant instance - ignore EAPOL-MKA"); return; } @@ -3137,10 +3408,14 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, */ struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, + Boolean macsec_replay_protect, u32 macsec_replay_window, u16 port, u8 priority, const char *ifname, const u8 *addr) { struct ieee802_1x_kay *kay; + wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR + " port=%u priority=%u", + ifname, MAC2STR(addr), port, priority); kay = os_zalloc(sizeof(*kay)); if (!kay) { wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); @@ -3161,6 +3436,8 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, os_strlcpy(kay->if_name, ifname, IFNAMSIZ); os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); kay->actor_sci.port = host_to_be16(port ? port : 0x0001); + wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s", + sci_txt(&kay->actor_sci)); kay->actor_priority = priority; /* While actor acts as a key server, shall distribute sakey */ @@ -3187,21 +3464,27 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; kay->macsec_desired = FALSE; kay->macsec_protect = FALSE; + kay->macsec_encrypt = FALSE; kay->macsec_validate = Disabled; kay->macsec_replay_protect = FALSE; kay->macsec_replay_window = 0; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; + kay->mka_hello_time = MKA_HELLO_TIME; } else { kay->macsec_desired = TRUE; kay->macsec_protect = TRUE; - kay->macsec_encrypt = policy == SHOULD_ENCRYPT; - kay->macsec_validate = Strict; - kay->macsec_replay_protect = FALSE; - kay->macsec_replay_window = 0; - if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF) + if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF && + policy == SHOULD_ENCRYPT) { + kay->macsec_encrypt = TRUE; kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; - else + } else { /* SHOULD_SECURE */ + kay->macsec_encrypt = FALSE; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; + } + kay->macsec_validate = Strict; + kay->macsec_replay_protect = macsec_replay_protect; + kay->macsec_replay_window = macsec_replay_window; + kay->mka_hello_time = MKA_HELLO_TIME; } wpa_printf(MSG_DEBUG, "KaY: state machine created"); @@ -3273,6 +3556,19 @@ ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) } +static const char * mode_txt(enum mka_created_mode mode) +{ + switch (mode) { + case PSK: + return "PSK"; + case EAP_EXCHANGE: + return "EAP"; + } + + return "?"; +} + + /** * ieee802_1x_kay_create_mka - */ @@ -3285,17 +3581,22 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_participant *participant; unsigned int usecs; + wpa_printf(MSG_DEBUG, + "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)", + kay->if_name, mode_txt(mode), yes_no(is_authenticator)); + 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"); + if (cak->len != 16 && cak->len != 32) { + wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u", + (unsigned int) cak->len); return NULL; } if (ckn->len > MAX_CKN_LEN) { - wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)"); + wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)"); return NULL; } if (!kay->enable) { @@ -3311,8 +3612,12 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, participant->ckn.len = ckn->len; os_memcpy(participant->ckn.name, ckn->name, ckn->len); + wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name, + participant->ckn.len); participant->cak.len = cak->len; os_memcpy(participant->cak.key, cak->key, cak->len); + wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key, + participant->cak.len); if (life) participant->cak_life = life + time(NULL); @@ -3362,6 +3667,8 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, if (!reset_participant_mi(participant)) goto fail; + wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s", + mi_txt(participant->mi)); participant->lrx = FALSE; participant->ltx = FALSE; @@ -3377,37 +3684,40 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, 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); + if (secy_create_transmit_sc(kay, participant->txsc)) + goto fail; /* to derive KEK from CAK and CKN */ - participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len; + participant->kek.len = participant->cak.len; if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key, + participant->cak.len, participant->ckn.name, participant->ckn.len, - participant->kek.key)) { - wpa_printf(MSG_ERROR, "KaY: Derived KEK failed"); + participant->kek.key, + participant->kek.len)) { + wpa_printf(MSG_ERROR, "KaY: KEK derivation 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; + participant->ick.len = participant->cak.len; if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key, + participant->cak.len, participant->ckn.name, participant->ckn.len, - participant->ick.key)) { - wpa_printf(MSG_ERROR, "KaY: Derived ICK failed"); + participant->ick.key, + participant->ick.len)) { + wpa_printf(MSG_ERROR, "KaY: ICK derivation 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); + usecs = os_random() % (kay->mka_hello_time * 1000); eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, participant, NULL); @@ -3425,6 +3735,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, return participant; fail: + os_free(participant->txsc); os_free(participant); return NULL; } @@ -3580,6 +3891,7 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, #ifdef CONFIG_CTRL_IFACE + /** * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details * @sm: Pointer to KaY allocated with ieee802_1x_kay_init() @@ -3588,19 +3900,24 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * - * Query KAY status information. This function fills in a text area with current + * Query KaY status information. This function fills in a text area with current * status information. If the buffer (buf) is not large enough, status * information will be truncated to fit the buffer. */ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, size_t buflen) { - int len; + char *pos, *end; + int res, count; + struct ieee802_1x_mka_participant *p; if (!kay) return 0; - len = os_snprintf(buf, buflen, + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "PAE KaY status=%s\n" "Authenticated=%s\n" "Secured=%s\n" @@ -3609,7 +3926,8 @@ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, "Key Server Priority=%u\n" "Is Key Server=%s\n" "Number of Keys Distributed=%u\n" - "Number of Keys Received=%u\n", + "Number of Keys Received=%u\n" + "MKA Hello Time=%u\n", kay->active ? "Active" : "Not-Active", kay->authenticated ? "Yes" : "No", kay->secured ? "Yes" : "No", @@ -3618,10 +3936,162 @@ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, kay->key_server_priority, kay->is_key_server ? "Yes" : "No", kay->dist_kn - 1, - kay->rcvd_keys); - if (os_snprintf_error(buflen, len)) + kay->rcvd_keys, + kay->mka_hello_time); + if (os_snprintf_error(buflen, res)) + return 0; + pos += res; + + res = os_snprintf(pos, end - pos, + "actor_sci=%s\n", sci_txt(&kay->actor_sci)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos += res; + + res = os_snprintf(pos, end - pos, + "key_server_sci=%s\n", sci_txt(&kay->key_server_sci)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos += res; + + count = 0; + dl_list_for_each(p, &kay->participant_list, + struct ieee802_1x_mka_participant, list) { + char *pos2 = pos; + + res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=", + count); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + count++; + + pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, + p->ckn.len); + + res = os_snprintf(pos2, end - pos2, + "\nmi=%s\n" + "mn=%u\n" + "active=%s\n" + "participant=%s\n" + "retain=%s\n" + "live_peers=%u\n" + "potential_peers=%u\n" + "is_key_server=%s\n" + "is_elected=%s\n", + mi_txt(p->mi), p->mn, + yes_no(p->active), + yes_no(p->participant), + yes_no(p->retain), + dl_list_len(&p->live_peers), + dl_list_len(&p->potential_peers), + yes_no(p->is_key_server), + yes_no(p->is_elected)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + pos = pos2; + } + + return pos - buf; +} + + +static const char * true_false(Boolean val) +{ + return val ? "true" : "false"; +} + + +static const char * activate_control_txt(enum activate_ctrl activate) +{ + switch (activate) { + case DEFAULT: + return "default"; + case DISABLED: + return "disabled"; + case ON_OPER_UP: + return "onOperUp"; + case ALWAYS: + return "always"; + } + + return "?"; +} + + +static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf, + char *end) +{ + char *pos = buf; + struct ieee802_1x_kay_peer *p; + int res; + + dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) { + res = os_snprintf(pos, end - pos, + "ieee8021XKayMkaPeerListMI=%s\n" + "ieee8021XKayMkaPeerListMN=%u\n" + "ieee8021XKayMkaPeerListType=%u\n" + "ieee8021XKayMkaPeerListSCI=%s\n", + mi_txt(p->mi), + p->mn, + live ? 1 : 2, + sci_txt(&p->sci)); + if (os_snprintf_error(end - pos, res)) + return pos; + pos += res; + } + + return pos; +} + + +int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf, + size_t buflen) +{ + char *pos, *end; + int res; + struct ieee802_1x_mka_participant *p; + + if (!kay) return 0; - return len; + pos = buf; + end = buf + buflen; + + dl_list_for_each(p, &kay->participant_list, + struct ieee802_1x_mka_participant, list) { + char *pos2 = pos; + + res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN="); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + + pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name, + p->ckn.len); + + res = os_snprintf(pos2, end - pos2, + "\nieee8021XKayMkaPartCached=%s\n" + "ieee8021XKayMkaPartActive=%s\n" + "ieee8021XKayMkaPartRetain=%s\n" + "ieee8021XKayMkaPartActivateControl=%s\n" + "ieee8021XKayMkaPartPrincipal=%s\n", + true_false(p->cached), + true_false(p->active), + true_false(p->retain), + activate_control_txt(p->activate), + true_false(p->principal)); + if (os_snprintf_error(buflen, res)) + return end - pos; + pos2 += res; + pos = pos2; + + pos = mka_mib_peer(&p->live_peers, TRUE, pos, end); + pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end); + } + + return pos - buf; } + #endif /* CONFIG_CTRL_IFACE */ diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h index b2650596cae3..3367d3aaa8c1 100644 --- a/src/pae/ieee802_1x_kay.h +++ b/src/pae/ieee802_1x_kay.h @@ -21,6 +21,7 @@ struct macsec_init_params; /* MKA timer, unit: millisecond */ #define MKA_HELLO_TIME 2000 +#define MKA_BOUNDED_HELLO_TIME 500 #define MKA_LIFE_TIME 6000 #define MKA_SAK_RETIRE_TIME 3000 @@ -38,7 +39,7 @@ struct ieee802_1x_mka_ki { struct ieee802_1x_mka_sci { u8 addr[ETH_ALEN]; be16 port; -}; +} STRUCT_PACKED; struct mka_key { u8 key[MAX_KEY_LEN]; @@ -149,6 +150,7 @@ struct ieee802_1x_kay_ctx { int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa); int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa); int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa); + int (*set_receive_lowest_pn)(void *ctx, struct receive_sa *sa); int (*create_receive_sc)(void *ctx, struct receive_sc *sc, enum validate_frames vf, enum confidentiality_offset co); @@ -187,6 +189,7 @@ struct ieee802_1x_kay { u32 macsec_replay_window; enum validate_frames macsec_validate; enum confidentiality_offset macsec_confidentiality; + u32 mka_hello_time; u32 ltx_kn; u8 ltx_an; @@ -236,6 +239,7 @@ u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci); struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, + Boolean macsec_replay_protect, u32 macsec_replay_window, u16 port, u8 priority, const char *ifname, const u8 *addr); void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay); @@ -271,5 +275,7 @@ int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, size_t buflen); +int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf, + size_t buflen); #endif /* IEEE802_1X_KAY_H */ diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h index bc522d89852b..f9cd3f41b093 100644 --- a/src/pae/ieee802_1x_kay_i.h +++ b/src/pae/ieee802_1x_kay_i.h @@ -15,7 +15,7 @@ #define MKA_VERSION_ID 1 -/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */ +/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 (MKPDU parameter sets) */ enum mka_packet_type { MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID, MKA_LIVE_PEER_LIST = 1, @@ -39,7 +39,7 @@ struct ieee802_1x_kay; struct ieee802_1x_mka_peer_id { u8 mi[MI_LEN]; be32 mn; -}; +} STRUCT_PACKED; struct ieee802_1x_kay_peer { struct ieee802_1x_mka_sci sci; @@ -51,6 +51,7 @@ struct ieee802_1x_kay_peer { Boolean macsec_desired; enum macsec_cap macsec_capability; Boolean sak_used; + int missing_sak_use_count; struct dl_list list; }; @@ -59,25 +60,24 @@ struct macsec_ciphersuite { 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 */ + int (*cak_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, u8 *cak, size_t cak_bytes); + int (*ckn_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, const u8 *sid, size_t sid_len, u8 *ckn); + int (*kek_trfm)(const u8 *cak, size_t cak_bytes, + const u8 *ckn, size_t ckn_len, + u8 *kek, size_t kek_bytes); + int (*ick_trfm)(const u8 *cak, size_t cak_bytes, + const u8 *ckn, size_t ckn_len, + u8 *ick, size_t ick_bytes); + int (*icv_hash)(const u8 *ick, size_t ick_bytes, + const u8 *msg, size_t msg_len, u8 *icv); }; #define DEFAULT_MKA_ALG_INDEX 0 @@ -95,7 +95,7 @@ struct ieee802_1x_mka_participant { Boolean retain; enum mka_created_mode mode; - enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; + enum activate_ctrl { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; /* used for active participant */ Boolean principal; @@ -131,8 +131,10 @@ struct ieee802_1x_mka_participant { u8 mi[MI_LEN]; u32 mn; + /* Current peer MI and SCI during MKPDU processing */ 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; @@ -165,7 +167,7 @@ struct ieee802_1x_mka_hdr { #endif /* octet 4 */ u8 length1; -}; +} STRUCT_PACKED; #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) @@ -210,9 +212,9 @@ struct ieee802_1x_mka_basic_body { be32 actor_mn; u8 algo_agility[4]; - /* followed by CAK Name*/ + /* followed by CAK Name */ u8 ckn[0]; -}; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List @@ -238,9 +240,9 @@ struct ieee802_1x_mka_peer_body { /* octet 4 */ u8 length1; - u8 peer[0]; /* followed by Peers */ -}; + u8 peer[0]; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure @@ -315,7 +317,7 @@ struct ieee802_1x_mka_sak_use_body { be32 okn; /* octet 41 - 44 */ be32 olpn; -}; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set @@ -362,7 +364,7 @@ struct ieee802_1x_mka_dist_sak_body { * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK */ u8 sak[0]; -}; +} STRUCT_PACKED; /** * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure @@ -398,7 +400,7 @@ struct ieee802_1x_mka_dist_cak_body { /* followed by CAK Name, 29- */ u8 ckn[0]; -}; +} STRUCT_PACKED; struct ieee802_1x_mka_icv_body { /* octet 1 */ @@ -418,6 +420,6 @@ struct ieee802_1x_mka_icv_body { /* octet 5 - */ u8 icv[0]; -}; +} STRUCT_PACKED; #endif /* IEEE802_1X_KAY_I_H */ diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c index 9a8d923d14f1..d63ca7f7038a 100644 --- a/src/pae/ieee802_1x_key.c +++ b/src/pae/ieee802_1x_key.c @@ -31,8 +31,9 @@ static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out) /* 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) +static int aes_kdf(const u8 *kdk, size_t kdk_bits, + const char *label, const u8 *context, + int ctx_bits, int ret_bits, u8 *ret) { const int h = 128; const int r = 8; @@ -40,6 +41,9 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, int lab_len, ctx_len, ret_len, buf_len; u8 *buf; + if (kdk_bits != 128 && kdk_bits != 256) + return -1; + lab_len = os_strlen(label); ctx_len = (ctx_bits + 7) / 8; ret_len = ((ret_bits & 0xffff) + 7) / 8; @@ -60,8 +64,14 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, WPA_PUT_BE16(&buf[buf_len - 2], ret_bits); for (i = 0; i < n; i++) { + int res; + buf[0] = (u8) (i + 1); - if (omac1_aes_128(kdk, buf, buf_len, ret)) { + if (kdk_bits == 128) + res = omac1_aes_128(kdk, buf, buf_len, ret); + else + res = omac1_aes_256(kdk, buf, buf_len, ret); + if (res) { os_free(buf); return -1; } @@ -72,33 +82,32 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context, } -/********** AES-CMAC-128 **********/ /** - * ieee802_1x_cak_128bits_aes_cmac + * ieee802_1x_cak_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) +int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, u8 *cak, size_t cak_bytes) { 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); + return aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CAK", + context, sizeof(context) * 8, 8 * cak_bytes, cak); } /** - * ieee802_1x_ckn_128bits_aes_cmac + * ieee802_1x_ckn_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 ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, const u8 *sid, + size_t sid_bytes, u8 *ckn) { int res; u8 *context; @@ -112,21 +121,21 @@ int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1, 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); + res = aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CKN", + context, ctx_len * 8, 128, ckn); os_free(context); return res; } /** - * ieee802_1x_kek_128bits_aes_cmac + * ieee802_1x_kek_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) +int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *kek, size_t kek_bytes) { u8 context[16]; @@ -134,19 +143,20 @@ int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn, 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); + return aes_kdf(cak, 8 * cak_bytes, "IEEE8021 KEK", + context, sizeof(context) * 8, + 8 * kek_bytes, kek); } /** - * ieee802_1x_ick_128bits_aes_cmac + * ieee802_1x_ick_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) +int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *ick, size_t ick_bytes) { u8 context[16]; @@ -154,22 +164,32 @@ int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn, 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); + return aes_kdf(cak, 8 *cak_bytes, "IEEE8021 ICK", + context, sizeof(context) * 8, + 8 * ick_bytes, ick); } /** - * ieee802_1x_icv_128bits_aes_cmac + * ieee802_1x_icv_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) +int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, 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"); + int res; + + if (ick_bytes == 16) + res = omac1_aes_128(ick, msg, msg_bytes, icv); + else if (ick_bytes == 32) + res = omac1_aes_256(ick, msg, msg_bytes, icv); + else + return -1; + if (res) { + wpa_printf(MSG_ERROR, + "MKA: AES-CMAC failed for ICV calculation"); return -1; } return 0; @@ -177,13 +197,14 @@ int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg, /** - * ieee802_1x_sak_128bits_aes_cmac + * ieee802_1x_sak_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) +int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx, + size_t ctx_bytes, u8 *sak, size_t sak_bytes) { - return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak); + return aes_kdf(cak, cak_bytes * 8, "IEEE8021 SAK", ctx, ctx_bytes * 8, + sak_bytes * 8, sak); } diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h index ea318ea4dde3..1f9058de5ccc 100644 --- a/src/pae/ieee802_1x_key.h +++ b/src/pae/ieee802_1x_key.h @@ -9,18 +9,18 @@ #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); +int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, u8 *cak, size_t cak_bytes); +int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1, + const u8 *mac2, const u8 *sid, + size_t sid_bytes, u8 *ckn); +int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *kek, size_t kek_bytes); +int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn, + size_t ckn_bytes, u8 *ick, size_t ick_bytes); +int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg, + size_t msg_bytes, u8 *icv); +int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx, + size_t ctx_bytes, u8 *sak, size_t sak_bytes); #endif /* IEEE802_1X_KEY_H */ diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c index ab5339bb2046..84ee42b05896 100644 --- a/src/pae/ieee802_1x_secy_ops.c +++ b/src/pae/ieee802_1x_secy_ops.c @@ -187,7 +187,7 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, ops = kay->ctx; if (!ops || !ops->get_transmit_next_pn) { wpa_printf(MSG_ERROR, - "KaY: secy get_receive_lowest_pn operation not supported"); + "KaY: secy get_transmit_next_pn operation not supported"); return -1; } @@ -208,7 +208,7 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, ops = kay->ctx; if (!ops || !ops->set_transmit_next_pn) { wpa_printf(MSG_ERROR, - "KaY: secy get_receive_lowest_pn operation not supported"); + "KaY: secy set_transmit_next_pn operation not supported"); return -1; } @@ -216,6 +216,27 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, } +int secy_set_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->set_receive_lowest_pn) { + wpa_printf(MSG_ERROR, + "KaY: secy set_receive_lowest_pn operation not supported"); + return -1; + } + + return ops->set_receive_lowest_pn(ops->ctx, rxsa); +} + + int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) { struct ieee802_1x_kay_ctx *ops; diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h index 9fb29c3ddfa0..2d112ba7c5d5 100644 --- a/src/pae/ieee802_1x_secy_ops.h +++ b/src/pae/ieee802_1x_secy_ops.h @@ -36,6 +36,8 @@ 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_set_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *txsa); 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); diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index a87ee745eb28..a3db4048c4a7 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -26,12 +26,12 @@ #define RADIUS_CLIENT_MAX_WAIT 120 /** - * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries + * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries * - * Maximum number of retransmit attempts before the entry is removed from + * Maximum number of server failovers before the entry is removed from * retransmit list. */ -#define RADIUS_CLIENT_MAX_RETRIES 10 +#define RADIUS_CLIENT_MAX_FAILOVER 3 /** * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages @@ -110,10 +110,15 @@ struct radius_msg_list { os_time_t next_try; /** - * attempts - Number of transmission attempts + * attempts - Number of transmission attempts for one server */ int attempts; + /** + * accu_attempts - Number of accumulated attempts + */ + int accu_attempts; + /** * next_wait - Next retransmission wait time in seconds */ @@ -367,9 +372,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, size_t prev_num_msgs; u8 *acct_delay_time; size_t acct_delay_time_len; + int num_servers; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { + num_servers = conf->num_acct_servers; if (radius->acct_sock < 0) radius_client_init_acct(radius); if (radius->acct_sock < 0 && conf->num_acct_servers > 1) { @@ -386,6 +393,7 @@ static int radius_client_retransmit(struct radius_client_data *radius, conf->acct_server->retransmissions++; } } else { + num_servers = conf->num_auth_servers; if (radius->auth_sock < 0) radius_client_init_auth(radius); if (radius->auth_sock < 0 && conf->num_auth_servers > 1) { @@ -449,7 +457,15 @@ static int radius_client_retransmit(struct radius_client_data *radius, } /* retransmit; remove entry if too many attempts */ + if (entry->accu_attempts > RADIUS_CLIENT_MAX_FAILOVER * + RADIUS_CLIENT_NUM_FAILOVER * num_servers) { + wpa_printf(MSG_INFO, + "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); + return 1; + } + entry->attempts++; + entry->accu_attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", radius_msg_get_hdr(entry->msg)->identifier); @@ -466,10 +482,6 @@ static int radius_client_retransmit(struct radius_client_data *radius, 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) { - wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); - return 1; - } return 0; } @@ -490,6 +502,30 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) return; os_get_reltime(&now); + + while (entry) { + if (now.sec >= entry->next_try) { + 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++; + else + auth_failover++; + } + } + entry = entry->next; + } + + if (auth_failover) + radius_client_auth_failover(radius); + + if (acct_failover) + radius_client_acct_failover(radius); + + entry = radius->msgs; first = 0; prev = NULL; @@ -517,17 +553,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) 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++; - else - auth_failover++; - } - if (first == 0 || entry->next_try < first) first = entry->next_try; @@ -538,6 +563,7 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) if (radius->msgs) { if (first < now.sec) first = now.sec; + eloop_cancel_timeout(radius_client_timer, radius, NULL); eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, @@ -545,12 +571,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) "retransmit in %ld seconds", (long int) (first - now.sec)); } - - if (auth_failover) - radius_client_auth_failover(radius); - - if (acct_failover) - radius_client_acct_failover(radius); } @@ -674,7 +694,10 @@ static void radius_client_list_add(struct radius_client_data *radius, entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; + entry->accu_attempts = 1; entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) + entry->next_wait = RADIUS_CLIENT_MAX_WAIT; entry->next = radius->msgs; radius->msgs = entry; radius_client_update_timeout(radius); @@ -713,9 +736,9 @@ static void radius_client_list_add(struct radius_client_data *radius, * * The message is added on the retransmission queue and will be retransmitted * automatically until a response is received or maximum number of retries - * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with - * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue - * automatically on transmission failure. + * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No + * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message + * is removed from the queue automatically on transmission failure. * * The related device MAC address can be used to identify pending messages that * can be removed with radius_client_flush_auth(). @@ -1087,14 +1110,13 @@ radius_change_server(struct radius_client_data *radius, } } - /* Reset retry counters for the new server */ - for (entry = radius->msgs; oserv && oserv != nserv && entry; - entry = entry->next) { + /* Reset retry counters */ + for (entry = radius->msgs; oserv && entry; entry = entry->next) { if ((auth && entry->msg_type != RADIUS_AUTH) || (!auth && entry->msg_type != RADIUS_ACCT)) continue; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; - entry->attempts = 0; + entry->attempts = 1; entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; } diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index e3afc0d53298..b621ada554ee 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-2014, Jouni Malinen + * Copyright (c) 2005-2009, 2011-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -357,6 +357,7 @@ struct radius_server_data { char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; char *t_c_server_url; @@ -380,6 +381,44 @@ 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); +#ifdef CONFIG_SQLITE +#ifdef CONFIG_HS20 + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_sim_provisioning(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE sim_provisioning(" + " mobile_identifier_hash TEXT PRIMARY KEY," + " imsi TEXT," + " mac_addr TEXT," + " eap_method TEXT," + " timestamp TEXT" + ");"; + + RADIUS_DEBUG("Adding database table for SIM provisioning information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + RADIUS_ERROR("SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + +#endif /* CONFIG_HS20 */ +#endif /* CONFIG_SQLITE */ + + void srv_log(struct radius_session *sess, const char *fmt, ...) PRINTF_FORMAT(2, 3); @@ -637,6 +676,23 @@ static void radius_server_testing_options(struct radius_session *sess, } +#ifdef CONFIG_ERP +static struct eap_server_erp_key * +radius_server_erp_find_key(struct radius_server_data *data, const char *keyname) +{ + 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; +} +#endif /* CONFIG_ERP */ + + static struct radius_session * radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, @@ -647,7 +703,7 @@ radius_server_get_new_session(struct radius_server_data *data, int res; struct radius_session *sess; struct eap_config eap_conf; - struct eap_user tmp; + struct eap_user *tmp; RADIUS_DEBUG("Creating a new session"); @@ -658,12 +714,27 @@ radius_server_get_new_session(struct radius_server_data *data, } RADIUS_DUMP_ASCII("User-Name", user, user_len); - 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); + tmp = os_zalloc(sizeof(*tmp)); + if (!tmp) + return NULL; + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp); +#ifdef CONFIG_ERP + if (res != 0 && data->erp) { + char *username; + + username = os_zalloc(user_len + 1); + if (username) { + os_memcpy(username, user, user_len); + if (radius_server_erp_find_key(data, username)) + res = 0; + os_free(username); + } + } +#endif /* CONFIG_ERP */ if (res != 0) { RADIUS_DEBUG("User-Name not found from user database"); + eap_user_free(tmp); return NULL; } @@ -671,10 +742,12 @@ radius_server_get_new_session(struct radius_server_data *data, sess = radius_server_new_session(data, client); if (sess == NULL) { RADIUS_DEBUG("Failed to create a new session"); + eap_user_free(tmp); return NULL; } - sess->accept_attr = tmp.accept_attr; - sess->macacl = tmp.macacl; + sess->accept_attr = tmp->accept_attr; + sess->macacl = tmp->macacl; + eap_user_free(tmp); sess->username = os_malloc(user_len * 4 + 1); if (sess->username == NULL) { @@ -866,6 +939,117 @@ static void db_update_last_msk(struct radius_session *sess, const char *msk) } +#ifdef CONFIG_HS20 + +static int radius_server_is_sim_method(struct radius_session *sess) +{ + const char *name; + + name = eap_get_method(sess->eap); + return name && + (os_strcmp(name, "SIM") == 0 || + os_strcmp(name, "AKA") == 0 || + os_strcmp(name, "AKA'") == 0); +} + + +static int radius_server_hs20_missing_sim_pps(struct radius_msg *request) +{ + u8 *buf, *pos, *end, type, sublen; + size_t len; + + buf = NULL; + for (;;) { + if (radius_msg_get_attr_ptr(request, + RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + return 0; + 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 */ + + if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION) + continue; + + RADIUS_DUMP("HS2.0 mobile device version", pos, sublen); + if (sublen < 1 + 2) + continue; + if (pos[0] == 0) + continue; /* Release 1 STA does not support provisioning + + */ + /* UpdateIdentifier 0 indicates no PPS MO */ + return WPA_GET_BE16(pos + 1) == 0; + } +} + + +#define HS20_MOBILE_ID_HASH_LEN 16 + +static int radius_server_sim_provisioning_session(struct radius_session *sess, + const u8 *hash) +{ +#ifdef CONFIG_SQLITE + char *sql; + char addr_txt[ETH_ALEN * 3]; + char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1]; + struct os_time now; + int res; + const char *imsi, *eap_method; + + if (!sess->server->db || + (!db_table_exists(sess->server->db, "sim_provisioning") && + db_table_create_sim_provisioning(sess->server->db) < 0)) + return -1; + + imsi = eap_get_imsi(sess->eap); + if (!imsi) + return -1; + + eap_method = eap_get_method(sess->eap); + if (!eap_method) + return -1; + + os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, + MAC2STR(sess->mac_addr)); + wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash, + HS20_MOBILE_ID_HASH_LEN); + + os_get_time(&now); + sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)", + hash_txt, imsi, addr_txt, eap_method, now.sec); + if (!sql) + return -1; + + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + res = -1; + } else { + res = 0; + } + sqlite3_free(sql); + return res; +#endif /* CONFIG_SQLITE */ + return -1; +} + +#endif /* CONFIG_HS20 */ + + static struct radius_msg * radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, @@ -979,6 +1163,48 @@ radius_server_encapsulate_eap(struct radius_server_data *data, buf, 0)) { RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); } + } else if (code == RADIUS_CODE_ACCESS_ACCEPT && + data->hs20_sim_provisioning_url && + radius_server_is_sim_method(sess) && + radius_server_hs20_missing_sim_pps(request)) { + u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN]; + size_t prefix_len, url_len; + + RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning"); + + if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) { + radius_msg_free(msg); + return NULL; + } + RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash", + hash, HS20_MOBILE_ID_HASH_LEN); + + if (radius_server_sim_provisioning_session(sess, hash) < 0) { + radius_msg_free(msg); + return NULL; + } + + prefix_len = os_strlen(data->hs20_sim_provisioning_url); + url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN; + buf = os_malloc(1 + url_len + 1); + if (!buf) { + radius_msg_free(msg); + return NULL; + } + pos = buf; + *pos++ = data->subscr_remediation_method; + os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len); + pos += prefix_len; + wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1, + hash, HS20_MOBILE_ID_HASH_LEN); + RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s", + (char *) &buf[1]); + 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); } if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { @@ -2059,7 +2285,7 @@ radius_server_read_clients(const char *client_file, int ipv6) entry->addr.s_addr = addr.s_addr; val = 0; for (i = 0; i < mask; i++) - val |= 1 << (31 - i); + val |= 1U << (31 - i); entry->mask.s_addr = htonl(val); } #ifdef CONFIG_IPV6 @@ -2173,6 +2399,9 @@ radius_server_init(struct radius_server_conf *conf) os_strdup(conf->subscr_remediation_url); } data->subscr_remediation_method = conf->subscr_remediation_method; + if (conf->hs20_sim_provisioning_url) + data->hs20_sim_provisioning_url = + os_strdup(conf->hs20_sim_provisioning_url); if (conf->t_c_server_url) data->t_c_server_url = os_strdup(conf->t_c_server_url); @@ -2293,6 +2522,7 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data->subscr_remediation_url); + os_free(data->hs20_sim_provisioning_url); os_free(data->t_c_server_url); #ifdef CONFIG_SQLITE @@ -2506,15 +2736,8 @@ 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; + return radius_server_erp_find_key(data, keyname); } diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 167bbf5b2881..53728f9d7bf2 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -233,6 +233,7 @@ struct radius_server_conf { char *subscr_remediation_url; u8 subscr_remediation_method; + char *hs20_sim_provisioning_url; char *t_c_server_url; }; diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index fdd522087dbc..d720f7b81144 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -263,7 +263,8 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR - " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx); + " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa), + entry->network_ctx, entry->akmp); wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, entry->fils_cache_id_set ? entry->fils_cache_id : NULL, entry->pmk, entry->pmk_len); diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 345b0c84d871..704c95e68616 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -1594,7 +1594,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, 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); + kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0); return 0; } diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index e0c913074c63..9163f61fa2f2 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -20,8 +20,10 @@ #include "crypto/sha512.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" #include "eap_common/eap_defs.h" #include "eapol_supp/eapol_supp_sm.h" +#include "drivers/driver.h" #include "wpa.h" #include "eloop.h" #include "preauth.h" @@ -382,6 +384,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, if (!sm->cur_pmksa) sm->cur_pmksa = sa; +#ifdef CONFIG_IEEE80211R + } else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) { + wpa_printf(MSG_DEBUG, + "FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol"); +#endif /* CONFIG_IEEE80211R */ } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " @@ -532,15 +539,25 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, 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 u8 *z = NULL; + size_t z_len = 0; + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_DPP2 + if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) { + z = wpabuf_head(sm->dpp_z); + z_len = wpabuf_len(sm->dpp_z); + } +#endif /* CONFIG_DPP2 */ + 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); + sm->pairwise_cipher, z, z_len); } @@ -618,6 +635,33 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in EAPOL-Key 2/4"); + goto failed; + } + + kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3); + if (!kde_buf) { + wpa_printf(MSG_WARNING, + "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4"); + goto failed; + } + + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + pos = kde + kde_len; + if (ocv_insert_oci_kde(&ci, &pos) < 0) + goto failed; + kde_len = pos - kde; + } +#endif /* CONFIG_OCV */ + #ifdef CONFIG_P2P if (sm->p2p) { kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); @@ -686,7 +730,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, * likelihood of the first preauth EAPOL-Start frame getting to * the target AP. */ - eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL); + if (!dl_list_empty(&sm->pmksa_candidates)) + eloop_register_timeout(1, 0, wpa_sm_start_preauth, + sm, NULL); } if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) { @@ -980,8 +1026,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, } os_memset(&gd, 0, sizeof(gd)); - wpa_supplicant_key_neg_complete(sm, sm->bssid, - key_info & WPA_KEY_INFO_SECURE); return 0; } @@ -1019,9 +1063,27 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm, broadcast_ether_addr, keyidx, 0, igtk->pn, sizeof(igtk->pn), igtk->igtk, len) < 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to configure IGTK to the driver"); - return -1; + if (keyidx == 0x0400 || keyidx == 0x0500) { + /* Assume the AP has broken PMF implementation since it + * seems to have swapped the KeyID bytes. The AP cannot + * be trusted to implement BIP correctly or provide a + * valid IGTK, so do not try to configure this key with + * swapped KeyID bytes. Instead, continue without + * configuring the IGTK so that the driver can drop any + * received group-addressed robust management frames due + * to missing keys. + * + * Normally, this error behavior would result in us + * disconnecting, but there are number of deployed APs + * with this broken behavior, so as an interoperability + * workaround, allow the connection to proceed. */ + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Ignore IGTK configuration error due to invalid IGTK KeyID byte order"); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } } if (wnm_sleep) { @@ -1418,6 +1480,26 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to get channel info to validate received OCI in EAPOL-Key 3/4"); + return; + } + + if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s", + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, &sm->ptk) < 0) { goto failed; @@ -1442,8 +1524,11 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { - wpa_supplicant_key_neg_complete(sm, sm->bssid, - key_info & WPA_KEY_INFO_SECURE); + /* No GTK to be set to the driver */ + } else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: No GTK KDE included in EAPOL-Key msg 3/4"); + goto failed; } else if (ie.gtk && wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { @@ -1458,6 +1543,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } + if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk) + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + if (ie.gtk) wpa_sm_set_rekey_offload(sm); @@ -1511,6 +1600,26 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, } maxkeylen = gd->gtk_len = ie.gtk_len - 2; +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2"); + return -1; + } + + if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s", + ocv_errorstr); + return -1; + } + } +#endif /* CONFIG_OCV */ + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) @@ -1631,11 +1740,17 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; u8 *rbuf, *key_mic; + size_t kde_len = 0; + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) + kde_len = OCV_OCI_KDE_LEN; +#endif /* CONFIG_OCV */ mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - hdrlen, &rlen, (void *) &reply); + hdrlen + kde_len, &rlen, (void *) &reply); if (rbuf == NULL) return -1; @@ -1657,7 +1772,27 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_REPLAY_COUNTER_LEN); key_mic = (u8 *) (reply + 1); - WPA_PUT_BE16(key_mic + mic_len, 0); + WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data Length */ + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in EAPOL-Key 2/2"); + os_free(rbuf); + return -1; + } + + pos = key_mic + mic_len + 2; /* Key Data */ + if (ocv_insert_oci_kde(&ci, &pos) < 0) { + os_free(rbuf); + return -1; + } + } +#endif /* CONFIG_OCV */ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL, @@ -1755,7 +1890,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ } else { +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ ok = 1; sm->tptk_set = 0; sm->ptk_set = 1; @@ -1781,8 +1924,16 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore Key MIC failure for fuzz testing"); + goto continue_fuzz2; +#endif /* TEST_FUZZ */ return -1; } +#ifdef TEST_FUZZ + continue_fuzz2: +#endif /* TEST_FUZZ */ ok = 1; } @@ -1857,14 +2008,25 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, "WPA: No memory for AES-UNWRAP buffer"); return -1; } +#ifdef TEST_FUZZ + os_memset(buf, 0x11, *key_data_len); +#endif /* TEST_FUZZ */ if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { +#ifdef TEST_FUZZ + wpa_printf(MSG_INFO, + "TEST: Ignore AES unwrap failure for fuzz testing"); + goto continue_fuzz; +#endif /* TEST_FUZZ */ bin_clear_free(buf, *key_data_len); wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: AES unwrap failed - " "could not decrypt EAPOL-Key key data"); return -1; } +#ifdef TEST_FUZZ + continue_fuzz: +#endif /* TEST_FUZZ */ os_memcpy(key_data, buf, *key_data_len); bin_clear_free(buf, *key_data_len); WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len); @@ -2513,6 +2675,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_OWE crypto_ecdh_deinit(sm->owe_ecdh); #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + wpabuf_clear_free(sm->dpp_z); +#endif /* CONFIG_DPP2 */ os_free(sm); } @@ -2554,6 +2719,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) wpa_ft_prepare_auth_request(sm, NULL); clear_keys = 0; + sm->ft_protocol = 1; + } else { + sm->ft_protocol = 0; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_FILS @@ -2618,6 +2786,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R sm->ft_reassoc_completed = 0; + sm->ft_protocol = 0; #endif /* CONFIG_IEEE80211R */ /* Keys are not needed in the WPA state machine anymore */ @@ -2864,6 +3033,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_MFP: sm->mfp = value; break; + case WPA_PARAM_OCV: + sm->ocv = value; + break; default: break; } @@ -2938,6 +3110,19 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm) } +int wpa_sm_ocv_enabled(struct wpa_sm *sm) +{ + struct wpa_ie_data rsn; + + if (!sm->ocv || !sm->ap_rsn_ie) + return 0; + + return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, + &rsn) >= 0 && + (rsn.capabilities & WPA_CAPABILITY_OCVC); +} + + /** * 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() @@ -3817,6 +4002,8 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ + if (sm->ocv) + capab |= WPA_CAPABILITY_OCVC; wpabuf_put_le16(buf, capab); /* PMKID Count */ @@ -3846,11 +4033,13 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) MAC2STR(sm->r1kh_id)); pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, - pos, use_sha384) < 0) { + sm->pmk_r1_name, use_sha384) < 0) { wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); return -1; } - wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN); #ifdef CONFIG_IEEE80211W if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { @@ -3951,6 +4140,26 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, /* TODO: FILS IP Address Assignment */ +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + u8 *pos; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "FILS: Failed to get channel info for OCI element"); + wpabuf_free(buf); + return NULL; + } + + pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN); + if (ocv_insert_extended_oci(&ci, pos) < 0) { + wpabuf_free(buf); + return NULL; + } + } +#endif /* CONFIG_OCV */ + wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf); *kek = sm->ptk.kek; @@ -4114,6 +4323,43 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) goto fail; } +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in FILS (Re)Association Response frame"); + goto fail; + } + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr); + goto fail; + } + } +#endif /* CONFIG_OCV */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { + struct wpa_ie_data rsn; + + /* Check that PMKR1Name derived by the AP matches */ + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0 || + !rsn.pmkid || rsn.num_pmkid != 1 || + os_memcmp(rsn.pmkid, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No RSNE[PMKR1Name] match in AssocResp"); + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ + /* Key Delivery */ if (!elems.key_delivery) { wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); @@ -4435,3 +4681,14 @@ void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id) } #endif /* CONFIG_FILS */ } + + +#ifdef CONFIG_DPP2 +void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z) +{ + if (sm) { + wpabuf_clear_free(sm->dpp_z); + sm->dpp_z = z ? wpabuf_dup(z) : NULL; + } +} +#endif /* CONFIG_DPP2 */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 21f4b17815e9..8903f8e14c69 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -18,6 +18,7 @@ struct wpa_sm; struct eapol_sm; struct wpa_config_blob; struct hostapd_freq_params; +struct wpa_channel_info; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ @@ -82,6 +83,7 @@ struct wpa_sm_ctx { int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src, const u8 *pkt, size_t pkt_len); + int (*channel_info)(void *ctx, struct wpa_channel_info *ci); }; @@ -95,7 +97,8 @@ enum wpa_sm_conf_params { WPA_PARAM_KEY_MGMT, WPA_PARAM_MGMT_GROUP, WPA_PARAM_RSN_ENABLED, - WPA_PARAM_MFP + WPA_PARAM_MFP, + WPA_PARAM_OCV }; struct rsn_supp_config { @@ -141,6 +144,7 @@ int wpa_sm_set_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); +int wpa_sm_ocv_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); @@ -279,6 +283,11 @@ static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) return 0; } +static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm) +{ + return 0; +} + static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { @@ -456,5 +465,6 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); +void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index b8d60e3208d0..7dcb1043bfcc 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -14,6 +14,8 @@ #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" +#include "drivers/driver.h" #include "wpa.h" #include "wpa_i.h" @@ -242,6 +244,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ + if (sm->ocv) + capab |= WPA_CAPABILITY_OCVC; WPA_PUT_LE16(pos, capab); pos += 2; @@ -323,6 +327,26 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, *pos++ = sm->r0kh_id_len; os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); pos += sm->r0kh_id_len; +#ifdef CONFIG_OCV + if (kck && wpa_sm_ocv_enabled(sm)) { + /* OCI sub-element in the third FT message */ + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in FTE"); + os_free(buf); + return NULL; + } + + *pos++ = FTIE_SUBELEM_OCI; + *pos++ = OCV_OCI_LEN; + if (ocv_insert_oci(&ci, &pos) < 0) { + os_free(buf); + return NULL; + } + } +#endif /* CONFIG_OCV */ *ftie_len = pos - ftie_len - 1; if (ric_ies) { @@ -961,6 +985,25 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(sm)) { + struct wpa_channel_info ci; + + if (wpa_sm_channel_info(sm, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in (Re)Assoc Response"); + return -1; + } + + if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return -1; + } + } +#endif /* CONFIG_OCV */ + sm->ft_reassoc_completed = 1; if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index b94b17a85a3a..0c5955c66f88 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -86,6 +86,7 @@ struct wpa_sm { int rsn_enabled; /* Whether RSN is enabled in configuration */ int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ + int ocv; /* Operating Channel Validation */ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; @@ -125,8 +126,9 @@ struct wpa_sm { u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; size_t r0kh_id_len; u8 r1kh_id[FT_R1KH_ID_LEN]; - int ft_completed; - int ft_reassoc_completed; + unsigned int ft_completed:1; + unsigned int ft_reassoc_completed:1; + unsigned int ft_protocol:1; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ int set_ptk_after_assoc; @@ -167,6 +169,10 @@ struct wpa_sm { struct crypto_ecdh *owe_ecdh; u16 owe_group; #endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP2 + struct wpabuf *dpp_z; +#endif /* CONFIG_DPP2 */ }; @@ -395,6 +401,14 @@ static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm, sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len); } +static inline int wpa_sm_channel_info(struct wpa_sm *sm, + struct wpa_channel_info *ci) +{ + if (!sm->ctx->channel_info) + return -1; + return sm->ctx->channel_info(sm->ctx->ctx, ci); +} + int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index a3410d15447a..ae9f4ca241d8 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -223,6 +223,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, if (sm->mfp == 2) capab |= WPA_CAPABILITY_MFPR; #endif /* CONFIG_IEEE80211W */ + if (sm->ocv) + capab |= WPA_CAPABILITY_OCVC; WPA_PUT_LE16(pos, capab); pos += 2; @@ -463,6 +465,17 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) { + ie->oci = pos + 2 + RSN_SELECTOR_LEN; + ie->oci_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#endif /* CONFIG_OCV */ + return 0; } diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index 0e72af56029e..9d53973a9431 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -53,6 +53,10 @@ struct wpa_eapol_ie_parse { const u8 *ip_addr_req; const u8 *ip_addr_alloc; #endif /* CONFIG_P2P */ +#ifdef CONFIG_OCV + const u8 *oci; + size_t oci_len; +#endif /* CONFIG_OCV */ }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff --git a/src/tls/asn1.c b/src/tls/asn1.c index cec109292d5a..822f87c18212 100644 --- a/src/tls/asn1.c +++ b/src/tls/asn1.c @@ -31,6 +31,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) pos = buf; end = buf + len; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier"); + return -1; + } hdr->identifier = *pos++; hdr->class = hdr->identifier >> 6; hdr->constructed = !!(hdr->identifier & (1 << 5)); @@ -51,6 +55,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } else hdr->tag = hdr->identifier & 0x1f; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Length"); + return -1; + } tmp = *pos++; if (tmp & 0x80) { if (tmp == 0xff) { diff --git a/src/tls/bignum.c b/src/tls/bignum.c index f3baafe1061d..1a87c82d5a72 100644 --- a/src/tls/bignum.c +++ b/src/tls/bignum.c @@ -119,10 +119,10 @@ int bignum_cmp(const struct bignum *a, const struct bignum *b) /** - * bignum_cmd_d - Compare bignum to standard integer + * bignum_cmp_d - Compare bignum to standard integer * @a: Bignum from bignum_init() * @b: Small integer - * Returns: 0 on success, -1 on failure + * Returns: -1 if a < b, 0 if a == b, 1 if a > b */ int bignum_cmp_d(const struct bignum *a, unsigned long b) { diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 76e19746b020..a147a54a3d10 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-2015, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -514,6 +514,8 @@ int tlsv1_client_established(struct tlsv1_client *conn) * tlsv1_client_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 client connection data from tlsv1_client_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -521,13 +523,26 @@ int tlsv1_client_established(struct tlsv1_client *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -538,9 +553,18 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index 40fa6c7fbdee..7fcc256f14aa 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -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-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_client * tlsv1_client_init(void); void tlsv1_client_deinit(struct tlsv1_client *conn); int tlsv1_client_established(struct tlsv1_client *conn); int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index e66f1a98896d..80874e59d1de 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -290,7 +290,7 @@ static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cred->cert_probe || conn->cert_in_cb) { + if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) { cert_buf = wpabuf_alloc_copy(cert->cert_start, cert->cert_len); ev.peer_cert.cert = cert_buf; diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index 04d895e61926..4a1147b69e76 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -72,6 +72,9 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *out_len = 0; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 540696904095..12dcc859a280 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-2014, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -164,7 +164,8 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); goto failed; } ct = pos[0]; @@ -204,6 +205,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); + conn->write_alerts++; } return msg; @@ -296,6 +298,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } tlsv1_server_log(conn, "Received alert %d:%d", out_pos[0], out_pos[1]); + conn->read_alerts++; if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -459,6 +462,8 @@ int tlsv1_server_established(struct tlsv1_server *conn) * tlsv1_server_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 server connection data from tlsv1_server_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -466,13 +471,26 @@ int tlsv1_server_established(struct tlsv1_server *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -483,9 +501,18 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -708,6 +735,24 @@ void tlsv1_server_set_log_cb(struct tlsv1_server *conn, } +int tlsv1_server_get_failed(struct tlsv1_server *conn) +{ + return conn->state == FAILED; +} + + +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn) +{ + return conn->read_alerts; +} + + +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn) +{ + return conn->write_alerts; +} + + #ifdef CONFIG_TESTING_OPTIONS void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) { diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h index 10e7699312b0..c9c0875ca330 100644 --- a/src/tls/tlsv1_server.h +++ b/src/tls/tlsv1_server.h @@ -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-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); void tlsv1_server_deinit(struct tlsv1_server *conn); int tlsv1_server_established(struct tlsv1_server *conn); int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len); @@ -48,6 +49,10 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, void tlsv1_server_set_log_cb(struct tlsv1_server *conn, void (*cb)(void *ctx, const char *msg), void *ctx); +int tlsv1_server_get_failed(struct tlsv1_server *conn); +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn); +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn); + 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 29c667877215..2622585d84d0 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -30,6 +30,8 @@ struct tlsv1_server { u8 alert_level; u8 alert_description; + int read_alerts, write_alerts; + struct crypto_public_key *client_rsa_key; struct tls_verify_hash verify; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 4aa8a019f3e6..e957678fc0d9 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -139,8 +139,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos = in_data; left = *in_len; - if (left < 4) + if (left < 4) { + tlsv1_server_log(conn, + "Truncated handshake message (expected ClientHello)"); goto decode_error; + } /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { @@ -157,8 +160,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 3; left -= 4; - if (len > left) + if (len > left) { + tlsv1_server_log(conn, + "Truncated ClientHello (len=%d left=%d)", + (int) len, (int) left); goto decode_error; + } /* body - ClientHello */ @@ -166,8 +173,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, end = pos + len; /* ProtocolVersion client_version */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, "Truncated ClientHello/client_version"); goto decode_error; + } conn->client_version = WPA_GET_BE16(pos); tlsv1_server_log(conn, "Client version %d.%d", conn->client_version >> 8, @@ -196,8 +205,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, tls_version_str(conn->rl.tls_version)); /* Random random */ - if (end - pos < TLS_RANDOM_LEN) + if (end - pos < TLS_RANDOM_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/client_random"); goto decode_error; + } os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; @@ -205,25 +216,36 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_random, TLS_RANDOM_LEN); /* SessionID session_id */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id len"); goto decode_error; - if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + } + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); pos += 1 + *pos; /* TODO: add support for session resumption */ /* CipherSuite cipher_suites<2..2^16-1> */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, + "Truncated ClientHello/cipher_suites len"); goto decode_error; + } num_suites = WPA_GET_BE16(pos); pos += 2; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", pos, num_suites); - if (num_suites & 1) + if (num_suites & 1) { + tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites"); goto decode_error; + } num_suites /= 2; cipher_suite = 0; @@ -259,11 +281,17 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->cipher_suite = cipher_suite; /* CompressionMethod compression_methods<1..2^8-1> */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods len"); goto decode_error; + } num_suites = *pos++; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", pos, num_suites); compr_null_found = 0; @@ -1217,6 +1245,7 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { tlsv1_server_log(conn, "Mismatch in verify_data"); + conn->state = FAILED; return -1; } diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index bdc6c1199238..8d36cf135391 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -26,7 +26,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) size_t len = 0; struct x509_certificate *cert; - cert = conn->cred->cert; + cert = conn->cred ? conn->cred->cert : NULL; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) @@ -53,6 +53,9 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index f80c9a358bf5..fa4d44229622 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -532,6 +532,8 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) } done: + if (pos < end) + *pos = '\0'; end[-1] = '\0'; } diff --git a/src/utils/Makefile b/src/utils/Makefile index 52efc5321fca..1ee2bee67f6b 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -19,6 +19,7 @@ LIB_OBJS= \ common.o \ crc32.o \ ip_addr.o \ + json.o \ radiotap.o \ trace.o \ uuid.o \ diff --git a/src/utils/base64.c b/src/utils/base64.c index 8eb4ba127d48..53a92f49ed83 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -1,12 +1,13 @@ /* * Base64 encoding/decoding (RFC1341) - * Copyright (c) 2005-2011, Jouni Malinen + * Copyright (c) 2005-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" +#include #include "os.h" #include "base64.h" @@ -27,6 +28,8 @@ static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, size_t olen; int line_len; + if (len >= SIZE_MAX / 4) + return NULL; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ if (add_pad) olen += olen / 72; /* line feeds */ diff --git a/src/utils/browser.c b/src/utils/browser.c index 9cf6152d6cbd..ad0b382fbc11 100644 --- a/src/utils/browser.c +++ b/src/utils/browser.c @@ -166,8 +166,7 @@ int hs20_web_browser(const char *url) 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_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client"); gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600); scroll = gtk_scrolled_window_new(NULL, NULL); diff --git a/src/utils/common.c b/src/utils/common.c index 1eb33705bef3..b9c8bfdb98e9 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1073,7 +1073,8 @@ size_t utf8_unescape(const char *inp, size_t in_size, in_size--; } - while (in_size--) { + while (in_size) { + in_size--; if (res_size >= out_size) return 0; @@ -1084,8 +1085,9 @@ size_t utf8_unescape(const char *inp, size_t in_size, return res_size; case '\\': - if (!in_size--) + if (!in_size) return 0; + in_size--; inp++; /* fall through */ @@ -1116,7 +1118,8 @@ size_t utf8_escape(const char *inp, size_t in_size, if (!in_size) in_size = os_strlen(inp); - while (in_size--) { + while (in_size) { + in_size--; if (res_size++ >= out_size) return 0; @@ -1221,3 +1224,28 @@ u8 rssi_to_rcpi(int rssi) return 220; return (rssi + 110) * 2; } + + +char * get_param(const char *cmd, const char *param) +{ + const char *pos, *end; + char *val; + size_t len; + + pos = os_strstr(cmd, param); + if (!pos) + return NULL; + + pos += os_strlen(param); + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + val = os_malloc(len + 1); + if (!val) + return NULL; + os_memcpy(val, pos, len); + val[len] = '\0'; + return val; +} diff --git a/src/utils/common.h b/src/utils/common.h index f824d001aeab..792a30ab9bbe 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -567,6 +567,7 @@ int is_ctrl_char(char c); int str_starts(const char *str, const char *start); u8 rssi_to_rcpi(int rssi); +char * get_param(const char *cmd, const char *param); /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/const_time.h b/src/utils/const_time.h new file mode 100644 index 000000000000..ab8f611ef693 --- /dev/null +++ b/src/utils/const_time.h @@ -0,0 +1,191 @@ +/* + * Helper functions for constant time operations + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * These helper functions can be used to implement logic that needs to minimize + * externally visible differences in execution path by avoiding use of branches, + * avoiding early termination or other time differences, and forcing same memory + * access pattern regardless of values. + */ + +#ifndef CONST_TIME_H +#define CONST_TIME_H + + +#if defined(__clang__) +#define NO_UBSAN_UINT_OVERFLOW \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#else +#define NO_UBSAN_UINT_OVERFLOW +#endif + + +/** + * const_time_fill_msb - Fill all bits with MSB value + * @val: Input value + * Returns: Value with all the bits set to the MSB of the input val + */ +static inline unsigned int const_time_fill_msb(unsigned int val) +{ + /* Move the MSB to LSB and multiple by -1 to fill in all bits. */ + return (val >> (sizeof(val) * 8 - 1)) * ~0U; +} + + +/* Returns: -1 if val is zero; 0 if val is not zero */ +static inline unsigned int const_time_is_zero(unsigned int val) + NO_UBSAN_UINT_OVERFLOW +{ + /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */ + return const_time_fill_msb(~val & (val - 1)); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline unsigned int const_time_eq(unsigned int a, unsigned int b) +{ + return const_time_is_zero(a ^ b); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline u8 const_time_eq_u8(unsigned int a, unsigned int b) +{ + return (u8) const_time_eq(a, b); +} + + +/** + * const_time_eq_bin - Constant time memory comparison + * @a: First buffer to compare + * @b: Second buffer to compare + * @len: Number of octets to compare + * Returns: -1 if buffers are equal, 0 if not + * + * This function is meant for comparing passwords or hash values where + * difference in execution time or memory access pattern could provide external + * observer information about the location of the difference in the memory + * buffers. The return value does not behave like memcmp(), i.e., + * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike + * memcmp(), the execution time of const_time_eq_bin() does not depend on the + * contents of the compared memory buffers, but only on the total compared + * length. + */ +static inline unsigned int const_time_eq_bin(const void *a, const void *b, + size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + size_t i; + u8 res = 0; + + for (i = 0; i < len; i++) + res |= aa[i] ^ bb[i]; + + return const_time_is_zero(res); +} + + +/** + * const_time_select - Constant time unsigned int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline unsigned int const_time_select(unsigned int mask, + unsigned int true_val, + unsigned int false_val) +{ + return (mask & true_val) | (~mask & false_val); +} + + +/** + * const_time_select_int - Constant time int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline int const_time_select_int(unsigned int mask, int true_val, + int false_val) +{ + return (int) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_u8 - Constant time u8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val) +{ + return (u8) const_time_select(mask, true_val, false_val); +} + + +/** + * const_time_select_s8 - Constant time s8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val) +{ + return (s8) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_bin - Constant time binary buffer selection copy + * @mask: 0 (false) or -1 (true) to identify which value to copy + * @true_val: Buffer to copy for the true case + * @false_val: Buffer to copy for the false case + * @len: Number of octets to copy + * @dst: Destination buffer for the copy + * + * This function copies the specified buffer into the destination buffer using + * operations with identical memory access pattern regardless of which buffer + * is being copied. + */ +static inline void const_time_select_bin(u8 mask, const u8 *true_val, + const u8 *false_val, size_t len, + u8 *dst) +{ + size_t i; + + for (i = 0; i < len; i++) + dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]); +} + + +static inline int const_time_memcmp(const void *a, const void *b, size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + int diff, res = 0; + unsigned int mask; + + if (len == 0) + return 0; + do { + len--; + diff = (int) aa[len] - (int) bb[len]; + mask = const_time_is_zero((unsigned int) diff); + res = const_time_select_int(mask, res, diff); + } while (len); + + return res; +} + +#endif /* CONST_TIME_H */ diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 436bc8c99338..bb375be1095e 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -224,22 +224,25 @@ static int eloop_sock_queue(int sock, eloop_event_type type) #ifdef CONFIG_ELOOP_KQUEUE -static int eloop_sock_queue(int sock, eloop_event_type type) -{ - int filter; - struct kevent ke; +static short event_type_kevent_filter(eloop_event_type type) +{ switch (type) { case EVENT_TYPE_READ: - filter = EVFILT_READ; - break; + return EVFILT_READ; case EVENT_TYPE_WRITE: - filter = EVFILT_WRITE; - break; + return EVFILT_WRITE; default: - filter = 0; + return 0; } - EV_SET(&ke, sock, filter, EV_ADD, 0, 0, 0); +} + + +static int eloop_sock_queue(int sock, eloop_event_type type) +{ + struct kevent ke; + + EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0); if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) { wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s", __func__, sock, strerror(errno)); @@ -247,6 +250,7 @@ static int eloop_sock_queue(int sock, eloop_event_type type) } return 0; } + #endif /* CONFIG_ELOOP_KQUEUE */ @@ -301,7 +305,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, #endif /* CONFIG_ELOOP_POLL */ #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) if (new_max_sock >= eloop.max_fd) { - next = eloop.max_fd == 0 ? 16 : eloop.max_fd * 2; + next = new_max_sock + 16; temp_table = os_realloc_array(eloop.fd_table, next, sizeof(struct eloop_sock)); if (temp_table == NULL) @@ -411,7 +415,8 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock)); #endif /* CONFIG_ELOOP_EPOLL */ #ifdef CONFIG_ELOOP_KQUEUE - EV_SET(&ke, sock, 0, EV_DELETE, 0, 0, 0); + EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0, + 0, 0); if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) { wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s", __func__, sock, strerror(errno)); diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index 58519ea8d248..e62fbf96bcb3 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -31,6 +31,14 @@ #endif /* EAP_TLS_OPENSSL */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *) x); +} +#endif /* OpenSSL < 1.1.0 */ + + struct http_ctx { void *ctx; struct xml_node_ctx *xml; @@ -446,6 +454,7 @@ sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st))) #define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \ sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i)) #else /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L #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)) @@ -456,6 +465,13 @@ sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i)) #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)) +#else +DEFINE_STACK_OF(LogotypeInfo) +DEFINE_STACK_OF(LogotypeImage) +DEFINE_STACK_OF(LogotypeAudio) +DEFINE_STACK_OF(HashAlgAndValue) +DEFINE_STACK_OF(ASN1_IA5STRING) +#endif #endif /* OPENSSL_IS_BORINGSSL */ @@ -486,7 +502,8 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, return; n->hash_len = ASN1_STRING_length(hash->hashValue); - n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len); + n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue), + n->hash_len); if (n->hash == NULL) { os_free(n->alg_oid); return; @@ -499,7 +516,7 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, os_free(n->hash); return; } - os_memcpy(n->uri, ASN1_STRING_data(uri), len); + os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len); n->uri[len] = '\0'; hcert->num_logo++; @@ -814,9 +831,9 @@ static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert, } wpa_hexdump(MSG_DEBUG, "logotypeExtn", - ASN1_STRING_data(os), ASN1_STRING_length(os)); + ASN1_STRING_get0_data(os), ASN1_STRING_length(os)); - data = ASN1_STRING_data(os); + data = ASN1_STRING_get0_data(os); logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os)); if (logo == NULL) { wpa_printf(MSG_INFO, "Failed to parse logotypeExtn"); @@ -1136,7 +1153,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - store = SSL_CTX_get_cert_store(s->ctx); + store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)); if (ctx->peer_issuer) { wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); debug_dump_cert("OpenSSL: Issuer certificate", @@ -1272,12 +1289,13 @@ static int ocsp_resp_cb(SSL *s, void *arg) } +#if OPENSSL_VERSION_NUMBER < 0x10100000L 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; + SSL_CTX *ssl = SSL_get_SSL_CTX(s); int ret; ssl->method = real_ssl_method; @@ -1288,6 +1306,7 @@ static int curl_patch_ssl_new(SSL *s) return ret; } +#endif /* OpenSSL < 1.1.0 */ #endif /* HAVE_OCSP */ @@ -1306,6 +1325,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm) SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb); SSL_CTX_set_tlsext_status_arg(ssl, ctx); +#if OPENSSL_VERSION_NUMBER < 0x10100000L /* * Use a temporary SSL_METHOD to get a callback on SSL_new() * from libcurl since there is no proper callback registration @@ -1315,6 +1335,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm) patch_ssl_method.ssl_new = curl_patch_ssl_new; real_ssl_method = ssl->method; ssl->method = &patch_ssl_method; +#endif /* OpenSSL < 1.1.0 */ } #endif /* HAVE_OCSP */ @@ -1351,7 +1372,7 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, #ifdef EAP_TLS_OPENSSL curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl); curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx); -#ifdef OPENSSL_IS_BORINGSSL +#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* For now, using the CURLOPT_SSL_VERIFYSTATUS option only * with BoringSSL since the OpenSSL specific callback hack to * enable OCSP is not available with BoringSSL. The OCSP diff --git a/src/utils/json.c b/src/utils/json.c index b9130d3a65ab..b64433959ff7 100644 --- a/src/utils/json.c +++ b/src/utils/json.c @@ -103,6 +103,11 @@ static char * json_parse_string(const char **json_pos, const char *end) return str; case '\\': pos++; + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "JSON: Truncated \\ escape"); + goto fail; + } switch (*pos) { case '"': case '\\': @@ -165,6 +170,8 @@ static int json_parse_number(const char **json_pos, const char *end, break; } } + if (pos == end) + pos--; if (pos < *json_pos) return -1; len = pos - *json_pos + 1; diff --git a/src/utils/list.h b/src/utils/list.h index ee2f4856950f..85aa5e39cfe1 100644 --- a/src/utils/list.h +++ b/src/utils/list.h @@ -1,6 +1,6 @@ /* * Doubly-linked list - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -76,8 +76,8 @@ static inline unsigned int dl_list_len(struct dl_list *list) dl_list_entry((list)->prev, type, member)) #define dl_list_for_each(item, list, type, member) \ - for (item = dl_list_entry((list)->next, type, member); \ - &item->member != (list); \ + for (item = dl_list_first((list), type, member); \ + item && item != dl_list_entry((list), type, member); \ item = dl_list_entry(item->member.next, type, member)) #define dl_list_for_each_safe(item, n, list, type, member) \ diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index ed6eb3c6b677..474c8a372205 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -430,22 +430,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n) } -char * os_strncpy(char *dest, const char *src, size_t n) -{ - char *d = dest; - - while (n--) { - *d = *src; - if (*src == '\0') - break; - d++; - src++; - } - - return dest; -} - - size_t os_strlcpy(char *dest, const char *src, size_t siz) { const char *s = src; diff --git a/src/utils/os_none.c b/src/utils/os_none.c index e74f206a2c5a..5e0a3ada6678 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -218,12 +218,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n) } -char * os_strncpy(char *dest, const char *src, size_t n) -{ - return dest; -} - - size_t os_strlcpy(char *dest, const char *src, size_t size) { return 0; diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 1894fcdb0cf2..800c50772d89 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -1,6 +1,6 @@ /* * OS specific functions for UNIX/POSIX systems - * Copyright (c) 2005-2009, Jouni Malinen + * Copyright (c) 2005-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -250,6 +250,13 @@ void os_daemonize_terminate(const char *pid_file) int os_get_random(unsigned char *buf, size_t len) { +#ifdef TEST_FUZZ + size_t i; + + for (i = 0; i < len; i++) + buf[i] = i & 0xff; + return 0; +#else /* TEST_FUZZ */ FILE *f; size_t rc; @@ -266,6 +273,7 @@ int os_get_random(unsigned char *buf, size_t len) fclose(f); return rc != len ? -1 : 0; +#endif /* TEST_FUZZ */ } @@ -512,7 +520,7 @@ void * os_memdup(const void *src, size_t len) { void *r = os_malloc(len); - if (r) + if (r && src) os_memcpy(r, src, len); return r; } diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index 1b8ff82b4173..3af4fcde1daa 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/const_time.h" #include "common/ieee802_11_defs.h" #include "utils/bitfield.h" #include "utils/ext_password.h" @@ -919,6 +920,294 @@ static int json_tests(void) } +static int const_time_tests(void) +{ + struct const_time_fill_msb_test { + unsigned int val; + unsigned int expected; + } const_time_fill_msb_tests[] = { + { 0, 0 }, + { 1, 0 }, + { 2, 0 }, + { 1 << (sizeof(unsigned int) * 8 - 1), ~0 }, + { ~0 - 1, ~0 }, + { ~0, ~0 } + }; + struct const_time_is_zero_test { + unsigned int val; + unsigned int expected; + } const_time_is_zero_tests[] = { + { 0, ~0 }, + { 1, 0 }, + { 2, 0 }, + { 1 << (sizeof(unsigned int) * 8 - 1), 0 }, + { ~0 - 1, 0 }, + { ~0, 0 } + }; + struct const_time_eq_test { + unsigned int a; + unsigned int b; + unsigned int expected; + unsigned int expected_u8; + } const_time_eq_tests[] = { + { 0, 1, 0, 0 }, + { 1, 2, 0, 0 }, + { 1, 1, ~0, 0xff }, + { ~0, ~0, ~0, 0xff }, + { ~0, ~0 - 1, 0, 0 }, + { 0, 0, ~0, 0xff } + }; + struct const_time_eq_bin_test { + u8 *a; + u8 *b; + size_t len; + unsigned int expected; + } const_time_eq_bin_tests[] = { + { (u8 *) "", (u8 *) "", 0, ~0 }, + { (u8 *) "abcde", (u8 *) "abcde", 5, ~0 }, + { (u8 *) "abcde", (u8 *) "Abcde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "aBcde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abCde", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abcDe", 5, 0 }, + { (u8 *) "abcde", (u8 *) "abcdE", 5, 0 }, + { (u8 *) "\x00", (u8 *) "\x01", 1, 0 }, + { (u8 *) "\x00", (u8 *) "\x80", 1, 0 }, + { (u8 *) "\x00", (u8 *) "\x00", 1, ~0 } + }; + struct const_time_select_test { + unsigned int mask; + unsigned int true_val; + unsigned int false_val; + unsigned int expected; + } const_time_select_tests[] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, 0 }, + { ~0, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa }, + { 0, 0xaaaaaaaa, 0x55555555, 0x55555555 }, + { ~0, 3, 3, 3 }, + { 0, 3, 3, 3 }, + { ~0, 1, 2, 1 }, + { 0, 1, 2, 2 } + }; + struct const_time_select_int_test { + unsigned int mask; + int true_val; + int false_val; + int expected; + } const_time_select_int_tests[] = { + { ~0, -128, 127, -128 }, + { 0, -128, 127, 127 }, + { ~0, -2147483648, 2147483647, -2147483648 }, + { 0, -2147483648, 2147483647, 2147483647 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { ~0, -1, 1, -1 }, + { 0, -1, 1, 1 } + }; + struct const_time_select_u8_test { + u8 mask; + u8 true_val; + u8 false_val; + u8 expected; + } const_time_select_u8_tests[] = { + { ~0, ~0, ~0, ~0 }, + { 0, ~0, ~0, ~0 }, + { ~0, ~0, 0, ~0 }, + { 0, ~0, 0, 0 }, + { ~0, 0xaa, 0x55, 0xaa }, + { 0, 0xaa, 0x55, 0x55 }, + { ~0, 1, 2, 1 }, + { 0, 1, 2, 2 } + }; + struct const_time_select_s8_test { + u8 mask; + s8 true_val; + s8 false_val; + s8 expected; + } const_time_select_s8_tests[] = { + { ~0, -128, 127, -128 }, + { 0, -128, 127, 127 }, + { ~0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + { ~0, -1, 1, -1 }, + { 0, -1, 1, 1 } + }; + struct const_time_select_bin_test { + u8 mask; + u8 *true_val; + u8 *false_val; + size_t len; + u8 *expected; + } const_time_select_bin_tests[] = { + { ~0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "abcde" }, + { 0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "ABCDE" }, + { ~0, (u8 *) "", (u8 *) "", 0, (u8 *) "" }, + { 0, (u8 *) "", (u8 *) "", 0, (u8 *) "" } + }; + struct const_time_memcmp_test { + char *a; + char *b; + size_t len; + int expected; + } const_time_memcmp_tests[] = { + { "abcde", "abcde", 5, 0 }, + { "abcde", "bbcde", 5, -1 }, + { "bbcde", "abcde", 5, 1 }, + { "accde", "abcde", 5, 1 }, + { "abcee", "abcde", 5, 1 }, + { "abcdf", "abcde", 5, 1 }, + { "cbcde", "aXXXX", 5, 2 }, + { "a", "d", 1, -3 }, + { "", "", 0, 0 } + }; + unsigned int i; + int ret = 0; + + wpa_printf(MSG_INFO, "constant time tests"); + + for (i = 0; i < ARRAY_SIZE(const_time_fill_msb_tests); i++) { + struct const_time_fill_msb_test *test; + + test = &const_time_fill_msb_tests[i]; + if (const_time_fill_msb(test->val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_fill_msb(0x%x) test failed", + test->val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_is_zero_tests); i++) { + struct const_time_is_zero_test *test; + + test = &const_time_is_zero_tests[i]; + if (const_time_is_zero(test->val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_is_zero(0x%x) test failed", + test->val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_eq_tests); i++) { + struct const_time_eq_test *test; + + test = &const_time_eq_tests[i]; + if (const_time_eq(test->a, test->b) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_eq(0x%x,0x%x) test failed", + test->a, test->b); + ret = -1; + } + if (const_time_eq_u8(test->a, test->b) != test->expected_u8) { + wpa_printf(MSG_ERROR, + "const_time_eq_u8(0x%x,0x%x) test failed", + test->a, test->b); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_eq_bin_tests); i++) { + struct const_time_eq_bin_test *test; + + test = &const_time_eq_bin_tests[i]; + if (const_time_eq_bin(test->a, test->b, test->len) != + test->expected) { + wpa_printf(MSG_ERROR, + "const_time_eq_bin(len=%u) test failed", + (unsigned int) test->len); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_tests); i++) { + struct const_time_select_test *test; + + test = &const_time_select_tests[i]; + if (const_time_select(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_int_tests); i++) { + struct const_time_select_int_test *test; + + test = &const_time_select_int_tests[i]; + if (const_time_select_int(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_int(0x%x,%d,%d) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_u8_tests); i++) { + struct const_time_select_u8_test *test; + + test = &const_time_select_u8_tests[i]; + if (const_time_select_u8(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_u8(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_s8_tests); i++) { + struct const_time_select_s8_test *test; + + test = &const_time_select_s8_tests[i]; + if (const_time_select_s8(test->mask, test->true_val, + test->false_val) != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_select_s8(0x%x,0x%x,0x%x) test failed", + test->mask, test->true_val, test->false_val); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_select_bin_tests); i++) { + struct const_time_select_bin_test *test; + u8 dst[100]; + + test = &const_time_select_bin_tests[i]; + const_time_select_bin(test->mask, test->true_val, + test->false_val, test->len, dst); + if (os_memcmp(dst, test->expected, test->len) != 0) { + wpa_printf(MSG_ERROR, + "const_time_select_bin(0x%x,%u) test failed", + test->mask, (unsigned int) test->len); + ret = -1; + } + } + + for (i = 0; i < ARRAY_SIZE(const_time_memcmp_tests); i++) { + struct const_time_memcmp_test *test; + int res; + + test = &const_time_memcmp_tests[i]; + res = const_time_memcmp(test->a, test->b, test->len); + if (res != test->expected) { + wpa_printf(MSG_ERROR, + "const_time_memcmp(%s,%s,%d) test failed (%d != %d)", + test->a, test->b, (int) test->len, + res, test->expected); + ret = -1; + } + } + + return ret; +} + + int utils_module_tests(void) { int ret = 0; @@ -936,6 +1225,7 @@ int utils_module_tests(void) ip_addr_tests() < 0 || eloop_tests() < 0 || json_tests() < 0 || + const_time_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index a56462b8bbdc..c437000a7f50 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -422,6 +422,12 @@ static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, #ifdef CONFIG_ANDROID_LOG _wpa_hexdump(level, title, buf, len, show); #else /* CONFIG_ANDROID_LOG */ +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) { + _wpa_hexdump(level, title, buf, len, show); + return; + } +#endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { diff --git a/src/wps/wps.c b/src/wps/wps.c index 8d228270ff10..484df262c3ca 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -145,6 +145,8 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->peer_pubkey_hash_set = 1; } + data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta; + return data; } @@ -430,7 +432,7 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) if (wps_build_version(ie) || wps_build_req_type(ie, req_type) || - wps_build_wfa_ext(ie, 0, NULL, 0)) { + wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { wpabuf_free(ie); return NULL; } @@ -464,7 +466,7 @@ struct wpabuf * wps_build_assoc_resp_ie(void) if (wps_build_version(ie) || wps_build_resp_type(ie, WPS_RESP_AP) || - wps_build_wfa_ext(ie, 0, NULL, 0)) { + wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { wpabuf_free(ie); return NULL; } @@ -516,7 +518,7 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, wps_build_model_name(dev, ie) || wps_build_model_number(dev, ie) || wps_build_dev_name(dev, ie) || - wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) || + wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) || wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) || wps_build_secondary_dev_type(dev, ie) diff --git a/src/wps/wps.h b/src/wps/wps.h index 2505d2d9f246..14ce863269f6 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -100,6 +100,7 @@ struct wps_device_data { struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; int p2p; + u8 multi_ap_ext; }; /** @@ -187,6 +188,12 @@ struct wps_config { * peer_pubkey_hash - Peer public key hash or %NULL if not known */ const u8 *peer_pubkey_hash; + + /** + * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA + * enrollee + */ + int multi_ap_backhaul_sta; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -395,6 +402,37 @@ struct wps_registrar_config { * PSK is set for a network. */ int force_per_enrollee_psk; + + /** + * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul + * enrollee + * + * This SSID is used by the Registrar to fill in information for + * Credentials when the enrollee advertises it is a Multi-AP backhaul + * STA. + */ + const u8 *multi_ap_backhaul_ssid; + + /** + * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in + * octets + */ + size_t multi_ap_backhaul_ssid_len; + + /** + * multi_ap_backhaul_network_key - The Network Key (PSK) for the + * Multi-AP backhaul enrollee. + * + * This key can be either the ASCII passphrase (8..63 characters) or the + * 32-octet PSK (64 hex characters). + */ + const u8 *multi_ap_backhaul_network_key; + + /** + * multi_ap_backhaul_network_key_len - Length of + * multi_ap_backhaul_network_key in octets + */ + size_t multi_ap_backhaul_network_key_len; }; diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index 770f5e90cbde..4e872f37295c 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -60,7 +60,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) } 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); + if (wps->dh_privkey && 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"); @@ -203,7 +204,8 @@ 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) + const u8 *auth_macs, size_t auth_macs_count, + u8 multi_ap_subelem) { u8 *len; @@ -244,6 +246,14 @@ int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, MAC2STR(&auth_macs[i * ETH_ALEN])); } + if (multi_ap_subelem) { + wpa_printf(MSG_DEBUG, "WPS: * Multi-AP (0x%x)", + multi_ap_subelem); + wpabuf_put_u8(msg, WFA_ELEM_MULTI_AP); + wpabuf_put_u8(msg, 1); /* length */ + wpabuf_put_u8(msg, multi_ap_subelem); + } + WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); #ifdef CONFIG_WPS_TESTING diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 756d57e876c5..fd51635158ac 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -67,6 +67,17 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr, } attr->registrar_configuration_methods = pos; break; + case WFA_ELEM_MULTI_AP: + if (len != 1) { + wpa_printf(MSG_DEBUG, + "WPS: Invalid Multi-AP Extension length %u", + len); + return -1; + } + attr->multi_ap_ext = *pos; + wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x", + attr->multi_ap_ext); + break; default: wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor " "Extension subelement %u", id); diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h index 8188fe9173d4..4de27b26d4e2 100644 --- a/src/wps/wps_attr_parse.h +++ b/src/wps/wps_attr_parse.h @@ -97,6 +97,7 @@ struct wps_parse_attr { const u8 *cred[MAX_CRED_COUNT]; const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT]; const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT]; + u8 multi_ap_ext; }; int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index bcae1ba5887b..747dc4710b20 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -374,7 +374,7 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, (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)) { + wps_build_wfa_ext(plain, 0, NULL, 0, 0)) { os_free(data.new_psk); wpabuf_clear_free(plain); return NULL; @@ -421,7 +421,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, 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)) { + wps_build_wfa_ext(data, 0, NULL, 0, 0)) { wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " "token"); wpabuf_clear_free(data); @@ -586,7 +586,7 @@ struct wpabuf * wps_build_wsc_ack(struct wps_data *wps) wps_build_msg_type(msg, WPS_WSC_ACK) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -610,7 +610,7 @@ struct wpabuf * wps_build_wsc_nack(struct wps_data *wps) wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || wps_build_config_error(msg, wps->config_error) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -726,7 +726,7 @@ struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx, 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)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -809,7 +809,7 @@ struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx, 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)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -848,7 +848,7 @@ struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx, 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)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -900,7 +900,7 @@ struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx, 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)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index 301864da433d..9fccb4eeb5c1 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -152,7 +152,8 @@ enum { WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, WFA_ELEM_REQUEST_TO_ENROLL = 0x03, WFA_ELEM_SETTINGS_DELAY_TIME = 0x04, - WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05 + WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05, + WFA_ELEM_MULTI_AP = 0x06 }; /* Device Password ID */ diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c index 0d01211a261c..b209fea8a4f2 100644 --- a/src/wps/wps_dev_attr.c +++ b/src/wps/wps_dev_attr.c @@ -390,6 +390,14 @@ int wps_process_os_version(struct wps_device_data *dev, const u8 *ver) } +void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext) +{ + dev->multi_ap_ext = ext; + wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x", + dev->multi_ap_ext); +} + + int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands) { if (bands == NULL) { diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h index c9034addbcc6..a4b4173cdbaf 100644 --- a/src/wps/wps_dev_attr.h +++ b/src/wps/wps_dev_attr.h @@ -29,6 +29,7 @@ int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg); int wps_process_device_attrs(struct wps_device_data *dev, struct wps_parse_attr *attr); int wps_process_os_version(struct wps_device_data *dev, const u8 *ver); +void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext); int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands); void wps_device_data_free(struct wps_device_data *dev); int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg); diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 417507740d7a..80ed603fc384 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -105,6 +105,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; u16 config_methods; + u8 multi_ap_backhaul_sta = 0; if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; @@ -134,6 +135,9 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) WPS_CONFIG_PHY_PUSHBUTTON); } + if (wps->multi_ap_backhaul_sta) + multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA; + if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || @@ -152,7 +156,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) || wps_build_vendor_ext_m1(&wps->wps->dev, msg)) { wpabuf_free(msg); return NULL; @@ -190,7 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wps_build_msg_type(msg, WPS_M3) || wps_build_registrar_nonce(wps, msg) || wps_build_e_hash(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_free(msg); return NULL; @@ -223,7 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_e_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -393,7 +397,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) (wps->wps->ap && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -430,7 +434,7 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps) wps_build_msg_type(msg, WPS_WSC_DONE) || wps_build_enrollee_nonce(wps, msg) || wps_build_registrar_nonce(wps, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index affd6a4af38a..06a8fdaf3459 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1530,7 +1530,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || - wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_build_wfa_ext(msg, 0, auth_macs, count, 0) || wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; @@ -2048,7 +2048,7 @@ struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, data.wps = wps; data.use_cred = cred; if (wps_build_cred(&data, ret) || - wps_build_wfa_ext(ret, 0, NULL, 0)) { + wps_build_wfa_ext(ret, 0, NULL, 0, 0)) { wpabuf_free(ret); return NULL; } diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index fe0c60bd120b..2cf22d4b7a63 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -125,6 +125,8 @@ struct wps_data { int pbc_in_m1; struct wps_nfc_pw_token *nfc_pw_token; + + int multi_ap_backhaul_sta; }; @@ -163,7 +165,8 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, struct wpabuf *plain); int wps_build_version(struct wpabuf *msg); int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, - const u8 *auth_macs, size_t auth_macs_count); + const u8 *auth_macs, size_t auth_macs_count, + u8 multi_ap_subelem); int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type); int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg); int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 379925e3f0a9..0ac5b2831379 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -188,6 +188,37 @@ struct wps_registrar { #ifdef WPS_WORKAROUNDS struct os_reltime pbc_ignore_start; #endif /* WPS_WORKAROUNDS */ + + /** + * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul + * enrollee + * + * This SSID is used by the Registrar to fill in information for + * Credentials when the enrollee advertises it is a Multi-AP backhaul + * STA. + */ + u8 multi_ap_backhaul_ssid[SSID_MAX_LEN]; + + /** + * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in + * octets + */ + size_t multi_ap_backhaul_ssid_len; + + /** + * multi_ap_backhaul_network_key - The Network Key (PSK) for the + * Multi-AP backhaul enrollee. + * + * This key can be either the ASCII passphrase (8..63 characters) or the + * 32-octet PSK (64 hex characters). + */ + u8 *multi_ap_backhaul_network_key; + + /** + * multi_ap_backhaul_network_key_len - Length of + * multi_ap_backhaul_network_key in octets + */ + size_t multi_ap_backhaul_network_key_len; }; @@ -667,6 +698,22 @@ wps_registrar_init(struct wps_context *wps, reg->dualband = cfg->dualband; reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; + if (cfg->multi_ap_backhaul_ssid) { + os_memcpy(reg->multi_ap_backhaul_ssid, + cfg->multi_ap_backhaul_ssid, + cfg->multi_ap_backhaul_ssid_len); + reg->multi_ap_backhaul_ssid_len = + cfg->multi_ap_backhaul_ssid_len; + } + if (cfg->multi_ap_backhaul_network_key) { + reg->multi_ap_backhaul_network_key = + os_memdup(cfg->multi_ap_backhaul_network_key, + cfg->multi_ap_backhaul_network_key_len); + if (reg->multi_ap_backhaul_network_key) + reg->multi_ap_backhaul_network_key_len = + cfg->multi_ap_backhaul_network_key_len; + } + if (wps_set_ie(reg)) { wps_registrar_deinit(reg); return NULL; @@ -704,6 +751,8 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_registrar_flush(reg); wpabuf_clear_free(reg->extra_cred); + bin_clear_free(reg->multi_ap_backhaul_network_key, + reg->multi_ap_backhaul_network_key_len); os_free(reg); } @@ -1281,7 +1330,7 @@ static int wps_set_ie(struct wps_registrar *reg) 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, 0)) || - wps_build_wfa_ext(beacon, 0, auth_macs, count) || + wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, beacon)) { wpabuf_free(beacon); wpabuf_free(probe); @@ -1311,7 +1360,7 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || (reg->dualband && wps_build_rf_bands(®->wps->dev, probe, 0)) || - wps_build_wfa_ext(probe, 0, auth_macs, count) || + wps_build_wfa_ext(probe, 0, auth_macs, count, 0) || wps_build_vendor_ext(®->wps->dev, probe)) { wpabuf_free(beacon); wpabuf_free(probe); @@ -1592,6 +1641,7 @@ int wps_build_credential_wrap(struct wpabuf *msg, int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; + struct wps_registrar *reg = wps->wps->registrar; if (wps->wps->registrar->skip_cred_build) goto skip_cred_build; @@ -1603,6 +1653,29 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } os_memset(&wps->cred, 0, sizeof(wps->cred)); + if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA && + reg->multi_ap_backhaul_ssid_len) { + wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials"); + os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid, + reg->multi_ap_backhaul_ssid_len); + wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len; + /* Backhaul is always WPA2PSK */ + wps->cred.auth_type = WPS_AUTH_WPA2PSK; + wps->cred.encr_type = WPS_ENCR_AES; + /* Set MAC address in the Credential to be the Enrollee's MAC + * address + */ + os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); + if (reg->multi_ap_backhaul_network_key) { + os_memcpy(wps->cred.key, + reg->multi_ap_backhaul_network_key, + reg->multi_ap_backhaul_network_key_len); + wps->cred.key_len = + reg->multi_ap_backhaul_network_key_len; + } + goto use_provided; + } + os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len); wps->cred.ssid_len = wps->wps->ssid_len; @@ -1845,7 +1918,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) 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_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -1913,7 +1986,7 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps) wps_build_assoc_state(wps, msg) || wps_build_config_error(msg, err) || wps_build_os_version(&wps->wps->dev, msg) || - wps_build_wfa_ext(msg, 0, NULL, 0)) { + wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } @@ -1949,7 +2022,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_r_snonce1(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -1984,7 +2057,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_r_snonce2(wps, plain) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_free(msg); @@ -2021,7 +2094,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || - wps_build_wfa_ext(msg, 0, NULL, 0) || + wps_build_wfa_ext(msg, 0, NULL, 0, 0) || wps_build_authenticator(wps, msg)) { wpabuf_clear_free(plain); wpabuf_clear_free(msg); @@ -2705,6 +2778,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps->use_psk_key = 1; } #endif /* WPS_WORKAROUNDS */ + wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext); wps->state = SEND_M2; return WPS_CONTINUE; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 0c458c6adef9..ca893a43c64b 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -599,7 +599,7 @@ 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); - if (wps_build_wfa_ext(msg, 0, NULL, 0)) { + if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) { wpabuf_free(msg); return NULL; } diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c index 267b565e4784..5c12bce25239 100644 --- a/src/wps/wps_validate.c +++ b/src/wps/wps_validate.c @@ -1057,7 +1057,7 @@ static int wps_validate_cred(const u8 *cred, size_t len) } -static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num, +static int wps_validate_credential(const u8 *cred[], u16 len[], size_t num, int mandatory) { size_t i; diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index a6809956d86f..529693131308 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -207,6 +207,12 @@ L_CFLAGS += -DCONFIG_SUITEB192 NEED_SHA384=y endif +ifdef CONFIG_OCV +L_CFLAGS += -DCONFIG_OCV +OBJS += src/common/ocv.c +CONFIG_IEEE80211W=y +endif + ifdef CONFIG_IEEE80211W L_CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -253,6 +259,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS_SERVER=y NEED_BASE64=y +ifdef CONFIG_DPP2 +L_CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_OWE @@ -1416,44 +1425,25 @@ endif OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c endif -ifdef CONFIG_CTRL_IFACE_DBUS -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE -DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c -ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_old_handlers_wps.c -endif -DBUS_OBJS += dbus/dbus_dict_helpers.c -DBUS_CFLAGS += $(DBUS_INCLUDE) -endif - ifdef CONFIG_CTRL_IFACE_DBUS_NEW -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW -DBUS_OBJS ?= dbus/dbus_dict_helpers.c -DBUS_OBJS += dbus/dbus_new_helpers.c -DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +L_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +OBJS += dbus/dbus_dict_helpers.c +OBJS += dbus/dbus_new_helpers.c +OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +OBJS += dbus/dbus_common.c ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_new_handlers_wps.c +OBJS += dbus/dbus_new_handlers_wps.c endif ifdef CONFIG_P2P -DBUS_OBJS += dbus/dbus_new_handlers_p2p.c +OBJS += dbus/dbus_new_handlers_p2p.c endif ifdef CONFIG_CTRL_IFACE_DBUS_INTRO -DBUS_OBJS += dbus/dbus_new_introspect.c -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +OBJS += dbus/dbus_new_introspect.c +L_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO endif -DBUS_CFLAGS += $(DBUS_INCLUDE) +L_CFLAGS += $(DBUS_INCLUDE) endif -ifdef DBUS -DBUS_CFLAGS += -DCONFIG_DBUS -DBUS_OBJS += dbus/dbus_common.c -endif - -OBJS += $(DBUS_OBJS) -L_CFLAGS += $(DBUS_CFLAGS) - ifdef CONFIG_CTRL_IFACE_BINDER WPA_SUPPLICANT_USE_BINDER=y L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index bf4daaa4cb1e..89119e7bd18f 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,5 +1,74 @@ ChangeLog for wpa_supplicant +2019-04-21 - v2.8 + * SAE changes + - added support for SAE Password Identifier + - changed default configuration to enable only groups 19, 20, 21 + (i.e., disable groups 25 and 26) and disable all unsuitable groups + completely based on REVmd changes + - do not regenerate PWE unnecessarily when the AP uses the + anti-clogging token mechanisms + - fixed some association cases where both SAE and FT-SAE were enabled + on both the station and the selected AP + - started to prefer FT-SAE over SAE AKM if both are enabled + - started to prefer FT-SAE over FT-PSK if both are enabled + - fixed FT-SAE when SAE PMKSA caching is used + - reject use of unsuitable groups based on new implementation guidance + in REVmd (allow only FFC groups with prime >= 3072 bits and ECC + groups with prime >= 256) + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-1/] (CVE-2019-9494) + * EAP-pwd changes + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-2/] (CVE-2019-9495) + - verify server scalar/element + [https://w1.fi/security/2019-4/] (CVE-2019-9499) + - fix message reassembly issue with unexpected fragment + [https://w1.fi/security/2019-5/] + - enforce rand,mask generation rules more strictly + - fix a memory leak in PWE derivation + - disallow ECC groups with a prime under 256 bits (groups 25, 26, and + 27) + * fixed CONFIG_IEEE80211R=y (FT) build without CONFIG_FILS=y + * Hotspot 2.0 changes + - do not indicate release number that is higher than the one + AP supports + - added support for release number 3 + - enable PMF automatically for network profiles created from + credentials + * fixed OWE network profile saving + * fixed DPP network profile saving + * added support for RSN operating channel validation + (CONFIG_OCV=y and network profile parameter ocv=1) + * added Multi-AP backhaul STA support + * fixed build with LibreSSL + * number of MKA/MACsec fixes and extensions + * extended domain_match and domain_suffix_match to allow list of values + * fixed dNSName matching in domain_match and domain_suffix_match when + using wolfSSL + * started to prefer FT-EAP-SHA384 over WPA-EAP-SUITE-B-192 AKM if both + are enabled + * extended nl80211 Connect and external authentication to support + SAE, FT-SAE, FT-EAP-SHA384 + * fixed KEK2 derivation for FILS+FT + * extended client_cert file to allow loading of a chain of PEM + encoded certificates + * extended beacon reporting functionality + * extended D-Bus interface with number of new properties + * fixed a regression in FT-over-DS with mac80211-based drivers + * OpenSSL: allow systemwide policies to be overridden + * extended driver flags indication for separate 802.1X and PSK + 4-way handshake offload capability + * added support for random P2P Device/Interface Address use + * extended PEAP to derive EMSK to enable use with ERP/FILS + * extended WPS to allow SAE configuration to be added automatically + for PSK (wps_cred_add_sae=1) + * removed support for the old D-Bus interface (CONFIG_CTRL_IFACE_DBUS) + * extended domain_match and domain_suffix_match to allow list of values + * added a RSN workaround for misbehaving PMF APs that advertise + IGTK/BIP KeyID using incorrect byte order + * fixed PTK rekeying with FILS and FT + 2018-12-02 - v2.7 * fixed WPA packet number reuse with replayed messages and key reinstallation diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index c2e93e20b58a..e81238e3924e 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -55,7 +55,6 @@ ALL += systemd/wpa_supplicant.service ALL += systemd/wpa_supplicant@.service ALL += systemd/wpa_supplicant-nl80211@.service ALL += systemd/wpa_supplicant-wired@.service -ALL += dbus/fi.epitest.hostap.WPASupplicant.service ALL += dbus/fi.w1.wpa_supplicant1.service ifdef CONFIG_BUILD_WPA_CLIENT_SO ALL += libwpa_client.so @@ -240,6 +239,12 @@ CFLAGS += -DCONFIG_SUITEB192 NEED_SHA384=y endif +ifdef CONFIG_OCV +CFLAGS += -DCONFIG_OCV +OBJS += ../src/common/ocv.o +CONFIG_IEEE80211W=y +endif + ifdef CONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -286,6 +291,9 @@ NEED_SHA512=y NEED_JSON=y NEED_GAS_SERVER=y NEED_BASE64=y +ifdef CONFIG_DPP2 +CFLAGS += -DCONFIG_DPP2 +endif endif ifdef CONFIG_OWE @@ -1526,6 +1534,9 @@ endif ifdef CONFIG_NO_RANDOM_POOL CFLAGS += -DCONFIG_NO_RANDOM_POOL else +ifdef CONFIG_GETRANDOM +CFLAGS += -DCONFIG_GETRANDOM +endif OBJS += ../src/crypto/random.o endif @@ -1567,35 +1578,17 @@ endif OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o endif -ifdef CONFIG_CTRL_IFACE_DBUS -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE -DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o -ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_old_handlers_wps.o -endif -DBUS_OBJS += dbus/dbus_dict_helpers.o -ifndef DBUS_LIBS -DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) -endif -ifndef DBUS_INCLUDE -DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) -endif -DBUS_CFLAGS += $(DBUS_INCLUDE) -DBUS_INTERFACE=fi.epitest.hostap.WPASupplicant -endif - ifdef CONFIG_CTRL_IFACE_DBUS_NEW -DBUS=y -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW -DBUS_OBJS ?= dbus/dbus_dict_helpers.o -DBUS_OBJS += dbus/dbus_new_helpers.o -DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +OBJS += dbus/dbus_dict_helpers.o +OBJS += dbus/dbus_new_helpers.o +OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o +OBJS += dbus/dbus_common.o ifdef CONFIG_WPS -DBUS_OBJS += dbus/dbus_new_handlers_wps.o +OBJS += dbus/dbus_new_handlers_wps.o endif ifdef CONFIG_P2P -DBUS_OBJS += dbus/dbus_new_handlers_p2p.o +OBJS += dbus/dbus_new_handlers_p2p.o endif ifndef DBUS_LIBS DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) @@ -1604,21 +1597,12 @@ ifndef DBUS_INCLUDE DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) endif ifdef CONFIG_CTRL_IFACE_DBUS_INTRO -DBUS_OBJS += dbus/dbus_new_introspect.o -DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +OBJS += dbus/dbus_new_introspect.o +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO endif -DBUS_CFLAGS += $(DBUS_INCLUDE) -DBUS_INTERFACE=fi.w1.wpa_supplicant1 -endif - -ifdef DBUS -DBUS_CFLAGS += -DCONFIG_DBUS -DBUS_OBJS += dbus/dbus_common.o -endif - -OBJS += $(DBUS_OBJS) -CFLAGS += $(DBUS_CFLAGS) +CFLAGS += $(DBUS_INCLUDE) LIBS += $(DBUS_LIBS) +endif ifdef CONFIG_READLINE OBJS_c += ../src/utils/edit_readline.o @@ -1981,13 +1965,11 @@ else endif %.service: %.service.in - $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \ - -e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ @$(E) " sed" $< %@.service: %.service.arg.in - $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \ - -e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ @$(E) " sed" $< wpa_supplicant.exe: wpa_supplicant diff --git a/wpa_supplicant/README b/wpa_supplicant/README index 2a3265f21eaa..bbc86b137424 100644 --- a/wpa_supplicant/README +++ b/wpa_supplicant/README @@ -1,7 +1,7 @@ -WPA Supplicant +wpa_supplicant ============== -Copyright (c) 2003-2018, Jouni Malinen and contributors +Copyright (c) 2003-2019, Jouni Malinen and contributors All Rights Reserved. This program is licensed under the BSD license (the one with diff --git a/wpa_supplicant/README-DPP b/wpa_supplicant/README-DPP new file mode 100644 index 000000000000..6496733735d4 --- /dev/null +++ b/wpa_supplicant/README-DPP @@ -0,0 +1,195 @@ +Device Provisioning Protocol (DPP) +================================== + +This document describes how the Device Provisioning Protocol (DPP) +implementation in wpa_supplicant and hostapd can be configured and how +the STA device and AP can be configured to connect each other using DPP +Connector mechanism. + +Introduction to DPP +------------------- + +Device provisioning Protocol allows enrolling of interface-less devices +in a secure Wi-Fi network using many methods like QR code based +authentication( detailed below ), PKEX based authentication etc. In DPP +a Configurator is used to provide network credentials to the devices. +The three phases of DPP connection are authentication, configuration and +network introduction. + +Build config setup +------------------ + +The following changes must go in the config file used to compile hostapd +and wpa_supplicant. + +wpa_supplicant build config +--------------------------- + +Enable DPP and protected management frame in wpa_supplicant build config +file + +CONFIG_IEEE80211W=y +CONFIG_DPP=y + +hostapd build config +-------------------- + +Enable DPP and protected management frame in hostapd build config file + +CONFIG_IEEE80211W=y +CONFIG_DPP=y + +Configurator build config +------------------------- + +Any STA or AP device can act as a Configurator. Enable DPP and protected +managment frames in build config. For an AP to act as Configurator, +Interworking needs to be enabled. For wpa_supplicant it is not required. + +CONFIG_INTERWORKING=y + + +Sample supplicant config file before provisioning +------------------------------------------------- + +ctrl_interface=DIR=/var/run/wpa_supplicant +ctrl_interface_group=0 +update_config=1 +pmf=2 +dpp_config_processing=2 + +Sample hostapd config file before provisioning +---------------------------------------------- + +interface=wlan0 +driver=nl80211 +ctrl_interface=/var/run/hostapd +ssid=test +channel=1 +wpa=2 +wpa_key_mgmt=DPP +ieee80211w=1 +wpa_pairwise=CCMP +rsn_pairwise=CCMP + + +Pre-requisites +-------------- + +It is assumed that an AP and client station are up by running hostapd +and wpa_supplicant using respective config files. + + +Creating Configurator +--------------------- + +Add a Configurator over the control interface (wpa_cli/hostapd_cli) + +> dpp_configurator_add +(returns id) + +To get key of Configurator +> dpp_configurator_get_key + + +How to configure an enrollee using Configurator +----------------------------------------------- + +On enrollee side: + +Generate QR code for the device. Store the qr code id returned by the +command. + +> dpp_bootstrap_gen type=qrcode mac= chan= key= +(returns bootstrapping info id) + +Get QR Code of device using the bootstrap info id. +> dpp_bootstrap_get_uri + +Make device listen to DPP request (The central frequency of channel 1 is +2412) in case if enrollee is a client device. + +> dpp_listen + +On Configurator side: + +Enter the QR Code in the Configurator. +> dpp_qr_code "" + +On successfully adding QR Code, a bootstrapping info id is returned. + +Send provisioning request to enrollee. (conf is ap-dpp if enrollee is an +AP. conf is sta-dpp if enrollee is a client) +> dpp_auth_init peer= conf= configurator= + +The DPP values will be printed in the console. Save this values into the +config file. If the enrollee is an AP, we need to manually write these +values to the hostapd config file. If the enrollee is a client device, +these details can be automatically saved to config file using the +following command. + +> save_config + +To set values in runtime for AP enrollees + +> set dpp_connector +> set dpp_csign +> set dpp_netaccesskey + +To set values in runtime for client enrollees, set dpp_config_processing +to 2 in wpa_supplicant conf file. + +Once the values are set in run-time (if not set in run-time, but saved +in config files, they are taken up in next restart), the client device +will automatically connect to the already provisioned AP and connection +will be established. + + +Self-configuring a device +------------------------- + +It is possible for a device to configure itself if it is the +Configurator for the network. + +Create a Configurator in the device and use the dpp_configurator_sign +command to get DPP credentials. + +> dpp_configurator_add +(returns configurator id) +> dpp_configurator_sign conf= configurator= + + +Sample AP configuration files after provisioning +------------------------------------------------ + +interface=wlan0 +driver=nl80211 +ctrl_interface=/var/run/hostapd +ssid=test +channel=1 +wpa=2 +wpa_key_mgmt=DPP +ieee80211w=1 +wpa_pairwise=CCMP +rsn_pairwise=CCMP +dpp_connector= +dpp_csign= +dpp_netaccesskey= + + +Sample station configuration file after provisioning +---------------------------------------------------- + +ctrl_interface=DIR=/var/run/wpa_supplicant +ctrl_interface_group=0 +update_config=1 +pmf=2 +dpp_config_processing=2 +network={ + ssid="test" + key_mgmt=DPP + ieee80211w=2 + dpp_connector="" + dpp_netaccesskey= + dpp_csign= +} diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P index 23ac7fa056a4..55a60a296ad7 100644 --- a/wpa_supplicant/README-P2P +++ b/wpa_supplicant/README-P2P @@ -150,7 +150,7 @@ join-a-group style PD instead of GO Negotiation style PD. p2p_connect [display|keypad|p2ps] [persistent|persistent=] [join|auth] - [go_intent=<0..15>] [freq=] [ht40] [vht] [provdisc] [auto] + [go_intent=<0..15>] [freq=] [ht40] [vht] [he] [provdisc] [auto] [ssid=] Start P2P group formation with a discovered P2P peer. This includes @@ -262,7 +262,7 @@ Parameters definition: session_mac - Mandatory MAC address that owns/initiated the session p2p_group_add [persistent|persistent=] [freq=] - [ht40] [vht] + [ht40] [vht] [he] Set up a P2P group owner manually (i.e., without group owner negotiation with a specific peer). This is also known as autonomous @@ -558,7 +558,7 @@ Remove all local services from internal SD query processing. Invitation p2p_invite [persistent=|group=] [peer=address] - [go_dev_addr=address] [freq=] [ht40] [vht] + [go_dev_addr=address] [freq=] [ht40] [vht] [he] [pref=] Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index c97f591311d3..6536c110a3ca 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -91,9 +91,6 @@ CONFIG_EAP_PEAP=y CONFIG_EAP_TTLS=y # EAP-FAST -# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed -# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., -# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y # EAP-GTC @@ -280,6 +277,9 @@ CONFIG_L2_PACKET=linux # Driver support is also needed for IEEE 802.11w. CONFIG_IEEE80211W=y +# Support Operating Channel Validation +#CONFIG_OCV=y + # Select TLS implementation # openssl = OpenSSL (default) # gnutls = GnuTLS @@ -327,10 +327,6 @@ CONFIG_IEEE80211W=y #CONFIG_NDIS_EVENTS_INTEGRATED=y #PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" -# Add support for old DBus control interface -# (fi.epitest.hostap.WPASupplicant) -#CONFIG_CTRL_IFACE_DBUS=y - # Add support for new DBus control interface # (fi.w1.hostap.wpa_supplicant1) #CONFIG_CTRL_IFACE_DBUS_NEW=y @@ -487,8 +483,8 @@ CONFIG_P2P=y # Enable TDLS support CONFIG_TDLS=y -# Wi-Fi Direct -# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# Wi-Fi Display +# This can be used to enable Wi-Fi Display extensions for P2P using an external # program to control the additional information exchanges in the messages. CONFIG_WIFI_DISPLAY=y @@ -517,8 +513,6 @@ CONFIG_WIFI_DISPLAY=y #CONFIG_MBO=y # Fast Initial Link Setup (FILS) (IEEE 802.11ai) -# Note: This is an experimental and not yet complete implementation. This -# should not be enabled for production use. #CONFIG_FILS=y # Support RSN on IBSS networks diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index ea846a0fad4b..4e191694296b 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -336,6 +336,12 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, conf->supported_rates = list; } +#ifdef CONFIG_IEEE80211AX + if (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + conf->ieee80211ax = ssid->he; +#endif /* CONFIG_IEEE80211AX */ + bss->isolate = !wpa_s->conf->p2p_intra_bss; bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; @@ -494,6 +500,10 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->ieee80211w = ssid->ieee80211w; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + bss->ocv = ssid->ocv; +#endif /* CONFIG_OCV */ + #ifdef CONFIG_WPS /* * Enable WPS by default for open and WPA/WPA2-Personal network, but @@ -1379,13 +1389,16 @@ int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, int offset, int width, int cf1, int cf2) { - if (!wpa_s->ap_iface) - return; + struct hostapd_iface *iface = wpa_s->ap_iface; + if (!iface) + iface = wpa_s->ifmsh; + if (!iface) + return; wpa_s->assoc_freq = freq; if (wpa_s->current_ssid) wpa_s->current_ssid->frequency = freq; - hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, + hostapd_event_ch_switch(iface->bss[0], freq, ht, offset, width, cf1, cf2); } @@ -1582,10 +1595,14 @@ int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd) void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { - if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + struct hostapd_iface *iface = wpa_s->ap_iface; + + if (!iface) + iface = wpa_s->ifmsh; + if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); - hostapd_dfs_radar_detected(wpa_s->ap_iface, radar->freq, + hostapd_dfs_radar_detected(iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); @@ -1595,10 +1612,14 @@ void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { - if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + struct hostapd_iface *iface = wpa_s->ap_iface; + + if (!iface) + iface = wpa_s->ifmsh; + if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq); - hostapd_dfs_start_cac(wpa_s->ap_iface, radar->freq, + hostapd_dfs_start_cac(iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } @@ -1607,10 +1628,14 @@ void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s, void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { - if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + struct hostapd_iface *iface = wpa_s->ap_iface; + + if (!iface) + iface = wpa_s->ifmsh; + if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); - hostapd_dfs_complete_cac(wpa_s->ap_iface, 1, radar->freq, + hostapd_dfs_complete_cac(iface, 1, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } @@ -1619,10 +1644,14 @@ void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { - if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + struct hostapd_iface *iface = wpa_s->ap_iface; + + if (!iface) + iface = wpa_s->ifmsh; + if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); - hostapd_dfs_complete_cac(wpa_s->ap_iface, 0, radar->freq, + hostapd_dfs_complete_cac(iface, 0, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } @@ -1631,10 +1660,14 @@ void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { - if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + struct hostapd_iface *iface = wpa_s->ap_iface; + + if (!iface) + iface = wpa_s->ifmsh; + if (!iface || !iface->bss[0]) return; wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); - hostapd_dfs_nop_finished(wpa_s->ap_iface, radar->freq, + hostapd_dfs_nop_finished(iface, radar->freq, radar->ht_enabled, radar->chan_offset, radar->chan_width, radar->cf1, radar->cf2); } diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 3a41db98e5ba..9b19f37affa0 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -1,6 +1,6 @@ /* * BSS table - * Copyright (c) 2009-2015, Jouni Malinen + * Copyright (c) 2009-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1337,3 +1337,10 @@ const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss) return NULL; } #endif /* CONFIG_FILS */ + + +int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab) +{ + return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB), + capab); +} diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 5251b2c354e3..3ce8cd3f429a 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -1,6 +1,6 @@ /* * BSS table - * Copyright (c) 2009-2015, Jouni Malinen + * Copyright (c) 2009-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -148,6 +148,7 @@ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates); struct wpa_bss_anqp * wpa_bss_anqp_alloc(void); int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss); const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss); +int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab); static inline int bss_is_dmg(const struct wpa_bss *bss) { diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index c43960697dc3..2058175f885e 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1043,6 +1043,30 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, #endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_FILS */ +#ifdef CONFIG_DPP + if (ssid->key_mgmt & WPA_KEY_MGMT_DPP) { + ret = os_snprintf(pos, end - pos, "%sDPP", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_DPP */ + +#ifdef CONFIG_OWE + if (ssid->key_mgmt & WPA_KEY_MGMT_OWE) { + ret = os_snprintf(pos, end - pos, "%sOWE", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_OWE */ + if (pos == buf) { os_free(buf); buf = NULL; @@ -1978,16 +2002,21 @@ static int wpa_config_parse_mka_cak(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { - if (hexstr2bin(value, ssid->mka_cak, MACSEC_CAK_LEN) || - value[MACSEC_CAK_LEN * 2] != '\0') { + size_t len; + + len = os_strlen(value); + if (len > 2 * MACSEC_CAK_MAX_LEN || + (len != 2 * 16 && len != 2 * 32) || + hexstr2bin(value, ssid->mka_cak, len / 2)) { wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.", line, value); return -1; } - + ssid->mka_cak_len = len / 2; ssid->mka_psk_set |= MKA_PSK_SET_CAK; - wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak, MACSEC_CAK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak, + ssid->mka_cak_len); return 0; } @@ -1996,8 +2025,18 @@ static int wpa_config_parse_mka_ckn(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { - if (hexstr2bin(value, ssid->mka_ckn, MACSEC_CKN_LEN) || - value[MACSEC_CKN_LEN * 2] != '\0') { + size_t len; + + len = os_strlen(value); + if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */ + len < 2 || /* too short */ + len % 2 != 0 /* not an integral number of bytes */) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", + line, value); + return -1; + } + ssid->mka_ckn_len = len / 2; + if (hexstr2bin(value, ssid->mka_ckn, ssid->mka_ckn_len)) { wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", line, value); return -1; @@ -2005,7 +2044,8 @@ static int wpa_config_parse_mka_ckn(const struct parse_data *data, ssid->mka_psk_set |= MKA_PSK_SET_CKN; - wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn, MACSEC_CKN_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn, + ssid->mka_ckn_len); return 0; } @@ -2018,7 +2058,7 @@ static char * wpa_config_write_mka_cak(const struct parse_data *data, if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK)) return NULL; - return wpa_config_write_string_hex(ssid->mka_cak, MACSEC_CAK_LEN); + return wpa_config_write_string_hex(ssid->mka_cak, ssid->mka_cak_len); } @@ -2027,7 +2067,7 @@ static char * wpa_config_write_mka_ckn(const struct parse_data *data, { if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN)) return NULL; - return wpa_config_write_string_hex(ssid->mka_ckn, MACSEC_CKN_LEN); + return wpa_config_write_string_hex(ssid->mka_ckn, ssid->mka_ckn_len); } #endif /* NO_CONFIG_WRITE */ @@ -2035,6 +2075,43 @@ static char * wpa_config_write_mka_ckn(const struct parse_data *data, #endif /* CONFIG_MACSEC */ +#ifdef CONFIG_OCV + +static int wpa_config_parse_ocv(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + char *end; + + ssid->ocv = strtol(value, &end, 0); + if (*end || ssid->ocv < 0 || ssid->ocv > 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid ocv value '%s'.", + line, value); + return -1; + } + if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION) + ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_ocv(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value = os_malloc(20); + + if (!value) + return NULL; + os_snprintf(value, 20, "%d", ssid->ocv); + value[20 - 1] = '\0'; + return value; +} +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_OCV */ + + static int wpa_config_parse_peerkey(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -2180,6 +2257,7 @@ static const struct parse_data ssid_fields[] = { { STR_KEYe(private_key_passwd) }, { STRe(dh_file) }, { STRe(subject_match) }, + { STRe(check_cert_subject) }, { STRe(altsubject_match) }, { STRe(domain_suffix_match) }, { STRe(domain_match) }, @@ -2190,6 +2268,7 @@ static const struct parse_data ssid_fields[] = { { STR_KEYe(private_key2_passwd) }, { STRe(dh_file2) }, { STRe(subject_match2) }, + { STRe(check_cert_subject2) }, { STRe(altsubject_match2) }, { STRe(domain_suffix_match2) }, { STRe(domain_match2) }, @@ -2238,6 +2317,9 @@ static const struct parse_data ssid_fields[] = { #ifdef CONFIG_IEEE80211W { INT_RANGE(ieee80211w, 0, 2) }, #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + { FUNC(ocv) }, +#endif /* CONFIG_OCV */ { FUNC(peerkey) /* obsolete - removed */ }, { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, @@ -2267,6 +2349,8 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(disable_sgi, 0, 1) }, { INT_RANGE(disable_ldpc, 0, 1) }, { INT_RANGE(ht40_intolerant, 0, 1) }, + { INT_RANGE(tx_stbc, -1, 1) }, + { INT_RANGE(rx_stbc, -1, 3) }, { INT_RANGE(disable_max_amsdu, -1, 1) }, { INT_RANGE(ampdu_factor, -1, 3) }, { INT_RANGE(ampdu_density, -1, 7) }, @@ -2299,6 +2383,8 @@ static const struct parse_data ssid_fields[] = { #ifdef CONFIG_MACSEC { INT_RANGE(macsec_policy, 0, 1) }, { INT_RANGE(macsec_integ_only, 0, 1) }, + { INT_RANGE(macsec_replay_protect, 0, 1) }, + { INT(macsec_replay_window) }, { INT_RANGE(macsec_port, 1, 65534) }, { INT_RANGE(mka_priority, 0, 255) }, { FUNC_KEY(mka_cak) }, @@ -2320,6 +2406,7 @@ static const struct parse_data ssid_fields[] = { #endif /* CONFIG_DPP */ { INT_RANGE(owe_group, 0, 65535) }, { INT_RANGE(owe_only, 0, 1) }, + { INT_RANGE(multi_ap_backhaul_sta, 0, 1) }, }; #undef OFFSET @@ -2440,6 +2527,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) str_clear_free(eap->private_key_passwd); os_free(eap->dh_file); os_free(eap->subject_match); + os_free(eap->check_cert_subject); os_free(eap->altsubject_match); os_free(eap->domain_suffix_match); os_free(eap->domain_match); @@ -2450,6 +2538,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) str_clear_free(eap->private_key2_passwd); os_free(eap->dh_file2); os_free(eap->subject_match2); + os_free(eap->check_cert_subject2); os_free(eap->altsubject_match2); os_free(eap->domain_suffix_match2); os_free(eap->domain_match2); @@ -2786,6 +2875,8 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->disable_ht40 = DEFAULT_DISABLE_HT40; ssid->disable_sgi = DEFAULT_DISABLE_SGI; ssid->disable_ldpc = DEFAULT_DISABLE_LDPC; + ssid->tx_stbc = DEFAULT_TX_STBC; + ssid->rx_stbc = DEFAULT_RX_STBC; ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU; ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR; ssid->ampdu_density = DEFAULT_AMPDU_DENSITY; @@ -2816,6 +2907,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER; #endif /* CONFIG_MACSEC */ ssid->mac_addr = -1; + ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH; } @@ -4440,6 +4532,21 @@ static int wpa_config_process_p2p_no_go_freq( return 0; } + +static int wpa_config_process_p2p_device_persistent_mac_addr( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + if (hwaddr_aton2(pos, config->p2p_device_persistent_mac_addr) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid p2p_device_persistent_mac_addr '%s'", + line, pos); + return -1; + } + + return 0; +} + #endif /* CONFIG_P2P */ @@ -4650,6 +4757,7 @@ static const struct global_parse_data global_fields[] = { { FUNC(os_version), CFG_CHANGED_OS_VERSION }, { STR(config_methods), CFG_CHANGED_CONFIG_METHODS }, { INT_RANGE(wps_cred_processing, 0, 2), 0 }, + { INT_RANGE(wps_cred_add_sae, 0, 1), 0 }, { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P @@ -4672,6 +4780,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 }, { INT(p2p_go_ht40), 0 }, { INT(p2p_go_vht), 0 }, + { INT(p2p_go_he), 0 }, { INT(p2p_disabled), 0 }, { INT_RANGE(p2p_go_ctwindow, 0, 127), 0 }, { INT(p2p_no_group_iface), 0 }, @@ -4681,6 +4790,9 @@ static const struct global_parse_data global_fields[] = { { IPV4(ip_addr_start), 0 }, { IPV4(ip_addr_end), 0 }, { INT_RANGE(p2p_cli_probe, 0, 1), 0 }, + { INT(p2p_device_random_mac_addr), 0 }, + { FUNC(p2p_device_persistent_mac_addr), 0 }, + { INT(p2p_interface_random_mac_addr), 0 }, #endif /* CONFIG_P2P */ { FUNC(country), CFG_CHANGED_COUNTRY }, { INT(bss_max_count), 0 }, diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index cd7571f59329..abbd8c90e2b5 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -745,6 +745,16 @@ struct wpa_config { */ int wps_cred_processing; + /** + * wps_cred_add_sae - Whether to enable SAE automatically for WPS + * + * 0 = only add the explicitly listed WPA2-PSK configuration + * 1 = add both the WPA2-PSK and SAE configuration and enable PMF so + * that the station gets configured in WPA3-Personal transition mode + * (supports both WPA2-Personal (PSK) and WPA3-Personal (SAE) APs). + */ + int wps_cred_add_sae; + #define MAX_SEC_DEVICE_TYPES 5 /** * sec_device_types - Secondary Device Types (P2P) @@ -1078,6 +1088,16 @@ struct wpa_config { */ int p2p_go_vht; + /** + * p2p_go_he - Default mode for 11ax HE enable when operating as GO + * + * This will take effect for p2p_group_add, p2p_connect, and p2p_invite. + * Note that regulatory constraints and driver capabilities are + * consulted anyway, so setting it to 1 can't do real harm. + * By default: 0 (disabled) + */ + int p2p_go_he; + /** * p2p_go_ctwindow - CTWindow to use when operating as GO * @@ -1478,6 +1498,35 @@ struct wpa_config { * 1 = enabled (true) */ int coloc_intf_reporting; + + /** + * p2p_device_random_mac_addr - P2P Device MAC address policy default + * + * 0 = use permanent MAC address + * 1 = use random MAC address on creating the interface if there is no + * persistent groups. + * + * By default, permanent MAC address is used. + */ + int p2p_device_random_mac_addr; + + /** + * p2p_device_persistent_mac_addr - Record last used MAC address + * + * If there are saved persistent groups, P2P cannot generate another + * random MAC address, and need to restore to last used MAC address. + */ + u8 p2p_device_persistent_mac_addr[ETH_ALEN]; + + /** + * p2p_interface_random_mac_addr - P2P Interface MAC address policy default + * + * 0 = use permanent MAC address + * 1 = use random MAC address on creating the interface. + * + * By default, permanent MAC address is used. + */ + int p2p_interface_random_mac_addr; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 09115e19dc2d..26f6ee147844 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -160,6 +160,15 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) errors++; } +#ifdef CONFIG_OCV + if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_ERROR, + "Line %d: PMF needs to be enabled whenever using OCV", + line); + errors++; + } +#endif /* CONFIG_OCV */ + return errors; } @@ -484,7 +493,7 @@ static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid) if (value == NULL) return; fprintf(f, "\t%s=%s\n", field, value); - os_free(value); + str_clear_free(value); } @@ -773,6 +782,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(private_key_passwd); STR(dh_file); STR(subject_match); + STR(check_cert_subject); STR(altsubject_match); STR(domain_suffix_match); STR(domain_match); @@ -783,6 +793,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(private_key2_passwd); STR(dh_file2); STR(subject_match2); + STR(check_cert_subject2); STR(altsubject_match2); STR(domain_suffix_match2); STR(domain_match2); @@ -829,7 +840,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(vht); INT_DEF(ht, 1); INT(ht40); - INT(max_oper_chwidth); + INT_DEF(max_oper_chwidth, DEFAULT_MAX_OPER_CHWIDTH); INT(vht_center_freq1); INT(vht_center_freq2); INT(pbss); @@ -853,6 +864,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_mka_cak(f, ssid); write_mka_ckn(f, ssid); INT(macsec_integ_only); + INT(macsec_replay_protect); + INT(macsec_replay_window); INT(macsec_port); INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER); #endif /* CONFIG_MACSEC */ @@ -880,12 +893,15 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) #endif /* CONFIG_DPP */ INT(owe_group); INT(owe_only); + INT(multi_ap_backhaul_sta); #ifdef CONFIG_HT_OVERRIDES INT_DEF(disable_ht, DEFAULT_DISABLE_HT); INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40); INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI); INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC); INT(ht40_intolerant); + INT_DEF(tx_stbc, DEFAULT_TX_STBC); + INT_DEF(rx_stbc, DEFAULT_RX_STBC); INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU); INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR); INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY); @@ -1173,6 +1189,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_cred_processing) fprintf(f, "wps_cred_processing=%d\n", config->wps_cred_processing); + if (config->wps_cred_add_sae) + fprintf(f, "wps_cred_add_sae=%d\n", + config->wps_cred_add_sae); if (config->wps_vendor_ext_m1) { int i, len = wpabuf_len(config->wps_vendor_ext_m1); const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1); @@ -1248,6 +1267,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40); if (config->p2p_go_vht) fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht); + if (config->p2p_go_he) + fprintf(f, "p2p_go_he=%d\n", config->p2p_go_he); if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW) fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow); if (config->p2p_disabled) @@ -1514,6 +1535,15 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->coloc_intf_reporting) fprintf(f, "coloc_intf_reporting=%d\n", config->coloc_intf_reporting); + if (config->p2p_device_random_mac_addr) + fprintf(f, "p2p_device_random_mac_addr=%d\n", + config->p2p_device_random_mac_addr); + if (!is_zero_ether_addr(config->p2p_device_persistent_mac_addr)) + fprintf(f, "p2p_device_persistent_mac_addr=" MACSTR "\n", + MAC2STR(config->p2p_device_persistent_mac_addr)); + if (config->p2p_interface_random_mac_addr) + fprintf(f, "p2p_interface_random_mac_addr=%d\n", + config->p2p_interface_random_mac_addr); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index d2a52d760089..1b2b1f1a36f1 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -33,10 +33,13 @@ #define DEFAULT_DISABLE_HT40 0 #define DEFAULT_DISABLE_SGI 0 #define DEFAULT_DISABLE_LDPC 0 +#define DEFAULT_TX_STBC -1 /* no change */ +#define DEFAULT_RX_STBC -1 /* no change */ #define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */ #define DEFAULT_AMPDU_FACTOR -1 /* no change */ #define DEFAULT_AMPDU_DENSITY -1 /* no change */ #define DEFAULT_USER_SELECTED_SIM 1 +#define DEFAULT_MAX_OPER_CHWIDTH -1 struct psk_list_entry { struct dl_list list; @@ -457,6 +460,17 @@ struct wpa_ssid { enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + /** + * ocv - Enable/disable operating channel validation + * + * If this parameter is set to 1, stations will exchange OCI element + * to cryptographically verify the operating channel. Setting this + * parameter to 0 disables this option. Default value: 0. + */ + int ocv; +#endif /* CONFIG_OCV */ + /** * frequency - Channel frequency in megahertz (MHz) for IBSS * @@ -505,6 +519,8 @@ struct wpa_ssid { int vht; + int he; + int max_oper_chwidth; unsigned int vht_center_freq1; @@ -682,6 +698,22 @@ struct wpa_ssid { * By default (empty string): Use whatever the OS has configured. */ char *ht_mcs; + + /** + * tx_stbc - Indicate STBC support for TX streams + * + * Value: -1..1, by default (-1): use whatever the OS or card has + * configured. See IEEE Std 802.11-2016, 9.4.2.56.2. + */ + int tx_stbc; + + /** + * rx_stbc - Indicate STBC support for RX streams + * + * Value: -1..3, by default (-1): use whatever the OS or card has + * configured. See IEEE Std 802.11-2016, 9.4.2.56.2. + */ + int rx_stbc; #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES @@ -773,6 +805,33 @@ struct wpa_ssid { */ int macsec_integ_only; + /** + * macsec_replay_protect - Enable MACsec replay protection + * + * This setting applies only when MACsec is in use, i.e., + * - macsec_policy is enabled + * - the key server has decided to enable MACsec + * + * 0: Replay protection disabled (default) + * 1: Replay protection enabled + */ + int macsec_replay_protect; + + /** + * macsec_replay_window - MACsec replay protection window + * + * A window in which replay is tolerated, to allow receipt of frames + * that have been misordered by the network. + * + * This setting applies only when MACsec replay protection active, i.e., + * - macsec_replay_protect is enabled + * - the key server has decided to enable MACsec + * + * 0: No replay window, strict check (default) + * 1..2^32-1: number of packets that could be misordered + */ + u32 macsec_replay_window; + /** * macsec_port - MACsec port (in SCI) * @@ -792,14 +851,16 @@ struct wpa_ssid { /** * mka_ckn - MKA pre-shared CKN */ -#define MACSEC_CKN_LEN 32 - u8 mka_ckn[MACSEC_CKN_LEN]; +#define MACSEC_CKN_MAX_LEN 32 + size_t mka_ckn_len; + u8 mka_ckn[MACSEC_CKN_MAX_LEN]; /** * mka_cak - MKA pre-shared CAK */ -#define MACSEC_CAK_LEN 16 - u8 mka_cak[MACSEC_CAK_LEN]; +#define MACSEC_CAK_MAX_LEN 32 + size_t mka_cak_len; + u8 mka_cak[MACSEC_CAK_MAX_LEN]; #define MKA_PSK_SET_CKN BIT(0) #define MKA_PSK_SET_CAK BIT(1) @@ -937,6 +998,13 @@ struct wpa_ssid { * the selection attempts for OWE BSS exceed the configured threshold. */ int owe_transition_bss_select_count; + + /** + * multi_ap_backhaul_sta - Multi-AP backhaul STA + * 0 = normal (non-Multi-AP) station + * 1 = Multi-AP backhaul station + */ + int multi_ap_backhaul_sta; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 0ce1830b4ef2..6328e91b989e 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -255,6 +255,8 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) errors++; wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"), &config->wps_cred_processing); + wpa_config_read_reg_dword(hk, TEXT("wps_cred_add_sae"), + &config->wps_cred_add_sae); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P config->p2p_ssid_postfix = wpa_config_read_reg_string( @@ -604,6 +606,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) } wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"), config->wps_cred_processing, 0); + wpa_config_write_reg_dword(hk, TEXT("wps_cred_add_sae"), + config->wps_cred_add_sae, 0); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P wpa_config_write_reg_string(hk, "p2p_ssid_postfix", @@ -892,6 +896,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) STR(private_key_passwd); STR(dh_file); STR(subject_match); + STR(check_cert_subject); STR(altsubject_match); STR(ca_cert2); STR(ca_path2); @@ -900,6 +905,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) STR(private_key2_passwd); STR(dh_file2); STR(subject_match2); + STR(check_cert_subject2); STR(altsubject_match2); STR(phase1); STR(phase2); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 77a3133d8d56..198ac562d8b6 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / Control interface (shared code for all backends) - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -56,6 +56,7 @@ #include "drivers/driver.h" #include "mesh.h" #include "dpp_supplicant.h" +#include "sme.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -1167,8 +1168,11 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, #ifdef CONFIG_AP u8 *_p2p_dev_addr = NULL; #endif /* CONFIG_AP */ + char *pos; + int multi_ap = 0; - if (cmd == NULL || os_strcmp(cmd, "any") == 0) { + if (!cmd || os_strcmp(cmd, "any") == 0 || + os_strncmp(cmd, "any ", 4) == 0) { _bssid = NULL; #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { @@ -1180,18 +1184,29 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, } _p2p_dev_addr = p2p_dev_addr; #endif /* CONFIG_P2P */ + } else if (os_strncmp(cmd, "multi_ap=", 9) == 0) { + _bssid = NULL; + multi_ap = atoi(cmd + 9); } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", cmd); return -1; } + if (cmd) { + pos = os_strstr(cmd, " multi_ap="); + if (pos) { + pos += 10; + multi_ap = atoi(pos); + } + } + #ifdef CONFIG_AP if (wpa_s->ap_iface) return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr); #endif /* CONFIG_AP */ - return wpas_wps_start_pbc(wpa_s, _bssid, 0); + return wpas_wps_start_pbc(wpa_s, _bssid, 0, multi_ap); } @@ -2117,6 +2132,18 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += ret; } + if (wpa_s->connection_set && + (wpa_s->connection_ht || wpa_s->connection_vht || + wpa_s->connection_he)) { + ret = os_snprintf(pos, end - pos, + "wifi_generation=%u\n", + wpa_s->connection_he ? 6 : + (wpa_s->connection_vht ? 5 : 4)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + #ifdef CONFIG_AP if (wpa_s->ap_iface) { pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos, @@ -2912,6 +2939,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos += ret; } #endif /* CONFIG_FST */ + if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) { + ret = os_snprintf(pos, end - pos, "[UTF-8]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); @@ -3987,6 +4020,22 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, } #endif /* CONFIG_IEEE80211R */ #endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, " FT-PSK"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, " SAE"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ return pos - buf; } @@ -4387,6 +4436,26 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_FILS */ + if (os_strcmp(field, "multibss") == 0 && wpa_s->multi_bss_support) { + res = os_snprintf(buf, buflen, "MULTIBSS-STA"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } + +#ifdef CONFIG_DPP + if (os_strcmp(field, "dpp") == 0) { +#ifdef CONFIG_DPP2 + res = os_snprintf(buf, buflen, "DPP=2"); +#else /* CONFIG_DPP2 */ + res = os_snprintf(buf, buflen, "DPP=1"); +#endif /* CONFIG_DPP2 */ + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_DPP */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -4717,6 +4786,20 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += ret; } #endif /* CONFIG_FILS */ +#ifdef CONFIG_FST + if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { + ret = os_snprintf(pos, end - pos, "[FST]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } +#endif /* CONFIG_FST */ + if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_UTF_8_SSID)) { + ret = os_snprintf(pos, end - pos, "[UTF-8]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) @@ -5013,10 +5096,11 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, bss = NULL; dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id) { - if (i-- == 0) { + if (i == 0) { bss = tmp; break; } + i--; } } @@ -5502,6 +5586,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL; size_t group_ssid_len = 0; + int he; if (!wpa_s->global->p2p_init_wpa_s) return -1; @@ -5514,7 +5599,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, /* <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=] * [join] [auth] [go_intent=<0..15>] [freq=] [provdisc] - * [ht40] [vht] [auto] [ssid=] */ + * [ht40] [vht] [he] [auto] [ssid=] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -5545,6 +5630,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; + he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he; pos2 = os_strstr(pos, " go_intent="); if (pos2) { @@ -5615,7 +5701,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, auth, go_intent, freq, freq2, persistent_id, - pd, ht40, vht, max_oper_chwidth, + pd, ht40, vht, max_oper_chwidth, he, group_ssid, group_ssid_len); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); @@ -6171,7 +6257,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) struct wpa_ssid *ssid; u8 *_peer = NULL, peer[ETH_ALEN]; int freq = 0, pref_freq = 0; - int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; + int ht40, vht, he, max_oper_chwidth, chwidth = 0, freq2 = 0; id = atoi(cmd); pos = os_strstr(cmd, " peer="); @@ -6208,6 +6294,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; + he = (os_strstr(cmd, " he") != NULL) || wpa_s->conf->p2p_go_he; pos = os_strstr(cmd, "freq2="); if (pos) @@ -6222,7 +6309,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) return -1; return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht, - max_oper_chwidth, pref_freq); + max_oper_chwidth, pref_freq, he); } @@ -6270,7 +6357,8 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, int id, int freq, int vht_center_freq2, - int ht40, int vht, int vht_chwidth) + int ht40, int vht, int vht_chwidth, + int he) { struct wpa_ssid *ssid; @@ -6284,7 +6372,7 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, vht_center_freq2, 0, ht40, vht, - vht_chwidth, NULL, 0, 0); + vht_chwidth, he, NULL, 0, 0); } @@ -6293,6 +6381,7 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) int freq = 0, persistent = 0, group_id = -1; int vht = wpa_s->conf->p2p_go_vht; int ht40 = wpa_s->conf->p2p_go_ht40 || vht; + int he = wpa_s->conf->p2p_go_he; int max_oper_chwidth, chwidth = 0, freq2 = 0; char *token, *context = NULL; #ifdef CONFIG_ACS @@ -6315,6 +6404,8 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) } else if (os_strcmp(token, "vht") == 0) { vht = 1; ht40 = 1; + } else if (os_strcmp(token, "he") == 0) { + he = 1; } else if (os_strcmp(token, "persistent") == 0) { persistent = 1; } else { @@ -6340,6 +6431,8 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY; wpa_s->p2p_go_do_acs = 1; } + } else { + wpa_s->p2p_go_do_acs = 0; } #endif /* CONFIG_ACS */ @@ -6350,10 +6443,10 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, freq, freq2, ht40, vht, - max_oper_chwidth); + max_oper_chwidth, he); return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht, - max_oper_chwidth); + max_oper_chwidth, he); } @@ -7622,7 +7715,7 @@ static int wpas_ctrl_iface_get_pref_freq_list( wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)", - iface_type, buf); + iface_type, cmd); ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list); if (ret) @@ -7941,6 +8034,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->ric_ies); wpa_s->ric_ies = NULL; + + wpa_supplicant_update_channel_list(wpa_s, NULL); + + free_bss_tmp_disallowed(wpa_s); } @@ -8763,26 +8860,39 @@ static void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, struct iphdr ip; const u8 *pos; unsigned int i; + char extra[30]; - if (len != HWSIM_PACKETLEN) + if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpected length %d", + (int) len); return; + } eth = (const struct ether_header *) buf; os_memcpy(&ip, eth + 1, sizeof(ip)); pos = &buf[sizeof(*eth) + sizeof(ip)]; if (ip.ihl != 5 || ip.version != 4 || - ntohs(ip.tot_len) != HWSIM_IP_LEN) + ntohs(ip.tot_len) > HWSIM_IP_LEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpect IP header"); return; - - for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { - if (*pos != (u8) i) - return; - pos++; } - wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, - MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); + for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) { + if (*pos != (u8) i) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore mismatching payload"); + return; + } + pos++; + } + extra[0] = '\0'; + if (ntohs(ip.tot_len) != HWSIM_IP_LEN) + os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len)); + wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s", + MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra); } @@ -8826,7 +8936,7 @@ static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) { u8 dst[ETH_ALEN], src[ETH_ALEN]; - char *pos; + char *pos, *pos2; int used; long int val; u8 tos; @@ -8835,11 +8945,12 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) struct iphdr *ip; u8 *dpos; unsigned int i; + size_t send_len = HWSIM_IP_LEN; if (wpa_s->l2_test == NULL) return -1; - /* format: */ + /* format: [len=] */ pos = cmd; used = hwaddr_aton2(pos, dst); @@ -8853,11 +8964,19 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) return -1; pos += used; - val = strtol(pos, NULL, 0); + val = strtol(pos, &pos2, 0); if (val < 0 || val > 0xff) return -1; tos = val; + pos = os_strstr(pos2, " len="); + if (pos) { + i = atoi(pos + 5); + if (i < sizeof(*ip) || i > HWSIM_IP_LEN) + return -1; + send_len = i; + } + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); @@ -8868,17 +8987,17 @@ static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) ip->version = 4; ip->ttl = 64; ip->tos = tos; - ip->tot_len = htons(HWSIM_IP_LEN); + ip->tot_len = htons(send_len); ip->protocol = 1; ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) + for (i = 0; i < send_len - sizeof(*ip); i++) *dpos++ = i; if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2], - HWSIM_PACKETLEN) < 0) + sizeof(struct ether_header) + send_len) < 0) return -1; wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR @@ -9458,13 +9577,6 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, return -1; } - if ((wpa_s->mac_addr_rand_supported & type) != type) { - wpa_printf(MSG_INFO, - "CTRL: MAC_RAND_SCAN types=%u != supported=%u", - type, wpa_s->mac_addr_rand_supported); - return -1; - } - if (enable > 1) { wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN enable=<0/1> not specified"); @@ -9498,21 +9610,25 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } if (type & MAC_ADDR_RAND_SCAN) { - wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, - addr, mask); + if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, + addr, mask)) + return -1; } if (type & MAC_ADDR_RAND_SCHED_SCAN) { - wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, - addr, mask); + if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, + addr, mask)) + return -1; if (wpa_s->sched_scanning && !wpa_s->pno) wpas_scan_restart_sched_scan(wpa_s); } if (type & MAC_ADDR_RAND_PNO) { - wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, - addr, mask); + if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, + addr, mask)) + return -1; + if (wpa_s->pno) { wpas_stop_pno(wpa_s); wpas_start_pno(wpa_s); @@ -9858,6 +9974,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len += eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, reply_size - reply_len); +#ifdef CONFIG_MACSEC + reply_len += ieee802_1x_kay_get_mib( + wpa_s->kay, reply + reply_len, + reply_size - reply_len); +#endif /* CONFIG_MACSEC */ } } else if (os_strncmp(buf, "STATUS", 6) == 0) { reply_len = wpa_supplicant_ctrl_iface_status( @@ -10506,6 +10627,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "RESEND_ASSOC") == 0) { if (wpas_ctrl_resend_assoc(wpa_s) < 0) reply_len = -1; +#ifdef CONFIG_IEEE80211W + } else if (os_strcmp(buf, "UNPROT_DEAUTH") == 0) { + sme_event_unprot_disconnect( + wpa_s, wpa_s->bssid, NULL, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); +#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) @@ -10549,7 +10676,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { int res; - res = wpas_dpp_bootstrap_gen(wpa_s, buf + 18); + res = dpp_bootstrap_gen(wpa_s->dpp, buf + 18); if (res < 0) { reply_len = -1; } else { @@ -10558,12 +10685,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { - if (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0) + if (dpp_bootstrap_remove(wpa_s->dpp, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { const char *uri; - uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22)); + uri = dpp_bootstrap_get_uri(wpa_s->dpp, atoi(buf + 22)); if (!uri) { reply_len = -1; } else { @@ -10572,8 +10699,8 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { - reply_len = wpas_dpp_bootstrap_info(wpa_s, atoi(buf + 19), - reply, reply_size); + reply_len = dpp_bootstrap_info(wpa_s->dpp, atoi(buf + 19), + reply, reply_size); } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0) reply_len = -1; @@ -10586,7 +10713,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { int res; - res = wpas_dpp_configurator_add(wpa_s, buf + 20); + res = dpp_configurator_add(wpa_s->dpp, buf + 20); if (res < 0) { reply_len = -1; } else { @@ -10595,14 +10722,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { - if (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0) + if (dpp_configurator_remove(wpa_s->dpp, buf + 24) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { - if (wpas_dpp_configurator_sign(wpa_s, buf + 22) < 0) + if (wpas_dpp_configurator_sign(wpa_s, buf + 21) < 0) reply_len = -1; } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { - reply_len = wpas_dpp_configurator_get_key(wpa_s, atoi(buf + 25), - reply, reply_size); + reply_len = dpp_configurator_get_key_id(wpa_s->dpp, + atoi(buf + 25), + reply, reply_size); } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { int res; diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index b88c80a99551..71fe7ed6bef5 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -570,8 +570,8 @@ static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s, } } - if (gid_set && chown(dir, -1, gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s", + if (gid_set && lchown(dir, -1, gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface=%s,gid=%d]: %s", dir, (int) gid, strerror(errno)); goto fail; } @@ -638,8 +638,8 @@ static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s, } } - if (gid_set && chown(fname, -1, gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s", + if (gid_set && lchown(fname, -1, gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface=%s,gid=%d]: %s", fname, (int) gid, strerror(errno)); goto fail; } @@ -1235,9 +1235,9 @@ static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global, wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", (int) gid); } - if (chown(ctrl, -1, gid) < 0) { + if (lchown(ctrl, -1, gid) < 0) { wpa_printf(MSG_ERROR, - "chown[global_ctrl_interface=%s,gid=%d]: %s", + "lchown[global_ctrl_interface=%s,gid=%d]: %s", ctrl, (int) gid, strerror(errno)); goto fail; } diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile index f355ebef51d2..4d8700428dcb 100644 --- a/wpa_supplicant/dbus/Makefile +++ b/wpa_supplicant/dbus/Makefile @@ -36,7 +36,6 @@ CFLAGS += -DCONFIG_WPS endif CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW -CFLAGS += -DCONFIG_CTRL_IFACE_DBUS ifndef DBUS_LIBS DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) @@ -54,8 +53,6 @@ CFLAGS += $(DBUS_INCLUDE) LIB_OBJS= \ dbus_common.o \ - dbus_old.o \ - dbus_old_handlers.o \ dbus_new.o \ dbus_new_handlers.o \ dbus_new_helpers.o \ @@ -63,7 +60,6 @@ LIB_OBJS= \ dbus_dict_helpers.o ifdef CONFIG_WPS -LIB_OBJS += dbus_old_handlers_wps.o LIB_OBJS += dbus_new_handlers_wps.o endif diff --git a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf index 382dcb34318c..e81b495f4b99 100644 --- a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf +++ b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf @@ -3,11 +3,6 @@ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> - - - - - @@ -15,9 +10,6 @@ - - - diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c index 7ef6cad62aaf..efa6c7b20595 100644 --- a/wpa_supplicant/dbus/dbus_common.c +++ b/wpa_supplicant/dbus/dbus_common.c @@ -16,7 +16,6 @@ #include "dbus_common.h" #include "dbus_common_i.h" #include "dbus_new.h" -#include "dbus_old.h" #include "../wpa_supplicant_i.h" @@ -351,9 +350,6 @@ struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global) #ifdef CONFIG_CTRL_IFACE_DBUS_NEW wpas_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ -#ifdef CONFIG_CTRL_IFACE_DBUS - wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 || -#endif /* CONFIG_CTRL_IFACE_DBUS */ wpas_dbus_init_common_finish(priv) < 0) { wpas_dbus_deinit(priv); return NULL; @@ -372,9 +368,5 @@ void wpas_dbus_deinit(struct wpas_dbus_priv *priv) wpas_dbus_ctrl_iface_deinit(priv); #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ -#ifdef CONFIG_CTRL_IFACE_DBUS - /* TODO: is any deinit needed? */ -#endif /* CONFIG_CTRL_IFACE_DBUS */ - wpas_dbus_deinit_common(priv); } diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index d4deb0fe35f0..fc2fc2ef1b96 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -13,6 +13,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "wps/wps.h" +#include "ap/sta_info.h" #include "../config.h" #include "../wpa_supplicant_i.h" #include "../bss.h" @@ -128,7 +129,8 @@ void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv) * Notify listeners about event related with interface */ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, - const char *sig_name, int properties) + const char *sig_name, + dbus_bool_t properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; @@ -230,7 +232,7 @@ void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) */ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, const char *bss_obj_path, - const char *sig_name, int properties) + const char *sig_name, dbus_bool_t properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; @@ -364,7 +366,7 @@ void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s, */ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, int id, const char *sig_name, - int properties) + dbus_bool_t properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; @@ -1077,6 +1079,79 @@ void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, } +/** + * wpas_dbus_signal_station - Send an event signal related to a station object + * @wpa_s: %wpa_supplicant network interface data + * @station_obj_path: Station object path + * @sig_name: signal name - StationAdded or StationRemoved + * @properties: Whether to add second argument with object properties + * + * Notify listeners about event related with station. + */ +static void wpas_dbus_signal_station(struct wpa_supplicant *wpa_s, + const char *station_obj_path, + const char *sig_name, + dbus_bool_t properties) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + wpa_printf(MSG_DEBUG, "dbus: STA signal %s", sig_name); + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &station_obj_path) || + (properties && + !wpa_dbus_get_object_properties(iface, station_obj_path, + WPAS_DBUS_NEW_IFACE_STA, + &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + + +/** + * wpas_dbus_signal_station_added - Send a Station added signal + * @wpa_s: %wpa_supplicant network interface data + * @station_obj_path: new Station object path + * + * Notify listeners about adding new Station + */ +static void wpas_dbus_signal_station_added(struct wpa_supplicant *wpa_s, + const char *station_obj_path) +{ + wpas_dbus_signal_station(wpa_s, station_obj_path, "StationAdded", TRUE); +} + + +/** + * wpas_dbus_signal_station_removed - Send a Station removed signal + * @wpa_s: %wpa_supplicant network interface data + * @station_obj_path: Station object path + * + * Notify listeners about removing Station + */ +static void wpas_dbus_signal_station_removed(struct wpa_supplicant *wpa_s, + const char *station_obj_path) +{ + wpas_dbus_signal_station(wpa_s, station_obj_path, "StationRemoved", + FALSE); +} + + #ifdef CONFIG_P2P /** @@ -1882,7 +1957,7 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, */ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, int id, const char *sig_name, - int properties) + dbus_bool_t properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; @@ -2146,6 +2221,9 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, case WPAS_DBUS_PROP_BSSS: prop = "BSSs"; break; + case WPAS_DBUS_PROP_STATIONS: + prop = "Stations"; + break; case WPAS_DBUS_PROP_CURRENT_AUTH_MODE: prop = "CurrentAuthMode"; break; @@ -2153,10 +2231,26 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, prop = "DisconnectReason"; flush = TRUE; break; + case WPAS_DBUS_PROP_AUTH_STATUS_CODE: + prop = "AuthStatusCode"; + flush = TRUE; + break; case WPAS_DBUS_PROP_ASSOC_STATUS_CODE: prop = "AssocStatusCode"; flush = TRUE; break; + case WPAS_DBUS_PROP_ROAM_TIME: + prop = "RoamTime"; + break; + case WPAS_DBUS_PROP_ROAM_COMPLETE: + prop = "RoamComplete"; + break; + case WPAS_DBUS_PROP_SESSION_LENGTH: + prop = "SessionLength"; + break; + case WPAS_DBUS_PROP_BSS_TM_STATUS: + prop = "BSSTMStatus"; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -2238,6 +2332,41 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, } +/** + * wpas_dbus_sta_signal_prop_changed - Signals change of STA property + * @wpa_s: %wpa_supplicant network interface data + * @property: indicates which property has changed + * @address: unique BSS identifier + * + * Sends PropertyChanged signals with path, interface, and arguments depending + * on which property has changed. + */ +void wpas_dbus_sta_signal_prop_changed(struct wpa_supplicant *wpa_s, + enum wpas_dbus_bss_prop property, + u8 address[ETH_ALEN]) +{ + char path[WPAS_DBUS_OBJECT_PATH_MAX]; + char *prop; + + switch (property) { + case WPAS_DBUS_STA_PROP_ADDRESS: + prop = "Address"; + break; + default: + wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", + __func__, property); + return; + } + + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(address)); + + wpa_dbus_mark_property_changed(wpa_s->global->dbus, path, + WPAS_DBUS_NEW_IFACE_STA, prop); +} + + /** * wpas_dbus_signal_debug_level_changed - Signals change of debug param * @global: wpa_global structure @@ -2726,6 +2855,30 @@ static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { NULL, NULL }, + { + "RoamTime", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + wpas_dbus_getter_roam_time, + NULL, + NULL + }, + { + "RoamComplete", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + wpas_dbus_getter_roam_complete, + NULL, + NULL + }, + { + "SessionLength", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + wpas_dbus_getter_session_length, + NULL, + NULL + }, + { + "BSSTMStatus", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", + wpas_dbus_getter_bss_tm_status, + NULL, + NULL + }, { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2852,6 +3005,157 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, } +static const struct wpa_dbus_property_desc wpas_dbus_sta_properties[] = { + { "Address", WPAS_DBUS_NEW_IFACE_STA, "ay", + wpas_dbus_getter_sta_address, + NULL, NULL + }, + { "AID", WPAS_DBUS_NEW_IFACE_STA, "q", + wpas_dbus_getter_sta_aid, + NULL, NULL + }, + { "Capabilities", WPAS_DBUS_NEW_IFACE_STA, "q", + wpas_dbus_getter_sta_caps, + NULL, NULL + }, + { "RxPackets", WPAS_DBUS_NEW_IFACE_STA, "t", + wpas_dbus_getter_sta_rx_packets, + NULL, NULL + }, + { "TxPackets", WPAS_DBUS_NEW_IFACE_STA, "t", + wpas_dbus_getter_sta_tx_packets, + NULL, NULL + }, + { "RxBytes", WPAS_DBUS_NEW_IFACE_STA, "t", + wpas_dbus_getter_sta_rx_bytes, + NULL, NULL + }, + { "TxBytes", WPAS_DBUS_NEW_IFACE_STA, "t", + wpas_dbus_getter_sta_tx_bytes, + NULL, NULL + }, + { NULL, NULL, NULL, NULL, NULL, NULL } +}; + + +static const struct wpa_dbus_signal_desc wpas_dbus_sta_signals[] = { + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_STA, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + + +/** + * wpas_dbus_unregister_sta - Unregister a connected station from dbus + * @wpa_s: wpa_supplicant interface structure + * @sta: station MAC address + * Returns: 0 on success, -1 on failure + * + * Unregisters STA representing object from dbus. + */ +int wpas_dbus_unregister_sta(struct wpa_supplicant *wpa_s, const u8 *sta) +{ + struct wpas_dbus_priv *ctrl_iface; + char station_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (!wpa_s || !wpa_s->global) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (!ctrl_iface) + return 0; + + os_snprintf(station_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(sta)); + + wpa_printf(MSG_DEBUG, "dbus: Unregister STA object '%s'", + station_obj_path); + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, + station_obj_path)) { + wpa_printf(MSG_ERROR, "dbus: Cannot unregister STA object %s", + station_obj_path); + return -1; + } + + wpas_dbus_signal_station_removed(wpa_s, station_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATIONS); + + return 0; +} + + +/** + * wpas_dbus_register_sta - Register a connected station with dbus + * @wpa_s: wpa_supplicant interface structure + * @sta: station MAC address + * Returns: 0 on success, -1 on failure + * + * Registers STA representing object with dbus. + */ +int wpas_dbus_register_sta(struct wpa_supplicant *wpa_s, const u8 *sta) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + char station_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + struct sta_handler_args *arg; + + /* Do nothing if the control interface is not turned on */ + if (!wpa_s || !wpa_s->global) + return 0; + ctrl_iface = wpa_s->global->dbus; + if (!ctrl_iface) + return 0; + + os_snprintf(station_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(sta)); + + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); + goto err; + } + + arg = os_zalloc(sizeof(struct sta_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for handler"); + goto err; + } + arg->wpa_s = wpa_s; + arg->sta = sta; + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_sta_properties, wpas_dbus_sta_signals); + + wpa_printf(MSG_DEBUG, "dbus: Register STA object '%s'", + station_obj_path); + if (wpa_dbus_register_object_per_iface(ctrl_iface, station_obj_path, + wpa_s->ifname, obj_desc)) { + wpa_printf(MSG_ERROR, + "Cannot register STA dbus object %s", + station_obj_path); + goto err; + } + + wpas_dbus_signal_station_added(wpa_s, station_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATIONS); + + return 0; + +err: + free_dbus_object_desc(obj_desc); + return -1; +} + + static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) wpas_dbus_handler_scan, @@ -3472,6 +3776,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { NULL, NULL }, + { "AuthStatusCode", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_auth_status_code, + NULL, + NULL + }, { "AssocStatusCode", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", wpas_dbus_getter_assoc_status_code, NULL, @@ -3489,6 +3798,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { NULL }, #endif /* CONFIG_MESH */ + { "Stations", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", + wpas_dbus_getter_stas, + NULL, + NULL + }, { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -3758,6 +4072,19 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "StationAdded", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "StationRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, { { "path", "o", ARG_OUT }, @@ -4038,6 +4365,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { NULL, NULL }, + { "VSIE", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", + wpas_dbus_getter_p2p_peer_vsie, + NULL, + NULL + }, { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -4066,7 +4398,7 @@ static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { */ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr, const char *interface, - const char *sig_name, int properties) + const char *sig_name, dbus_bool_t properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 40ae133b225e..42db3892ed77 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -28,8 +28,14 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_CURRENT_NETWORK, WPAS_DBUS_PROP_CURRENT_AUTH_MODE, WPAS_DBUS_PROP_BSSS, + WPAS_DBUS_PROP_STATIONS, WPAS_DBUS_PROP_DISCONNECT_REASON, + WPAS_DBUS_PROP_AUTH_STATUS_CODE, WPAS_DBUS_PROP_ASSOC_STATUS_CODE, + WPAS_DBUS_PROP_ROAM_TIME, + WPAS_DBUS_PROP_ROAM_COMPLETE, + WPAS_DBUS_PROP_SESSION_LENGTH, + WPAS_DBUS_PROP_BSS_TM_STATUS, }; enum wpas_dbus_bss_prop { @@ -45,6 +51,10 @@ enum wpas_dbus_bss_prop { WPAS_DBUS_BSS_PROP_AGE, }; +enum wpas_dbus_sta_prop { + WPAS_DBUS_STA_PROP_ADDRESS, +}; + #define WPAS_DBUS_OBJECT_PATH_MAX 150 #define WPAS_DBUS_NEW_SERVICE "fi.w1.wpa_supplicant1" @@ -61,6 +71,9 @@ enum wpas_dbus_bss_prop { #define WPAS_DBUS_NEW_BSSIDS_PART "BSSs" #define WPAS_DBUS_NEW_IFACE_BSS WPAS_DBUS_NEW_INTERFACE ".BSS" +#define WPAS_DBUS_NEW_STAS_PART "Stations" +#define WPAS_DBUS_NEW_IFACE_STA WPAS_DBUS_NEW_INTERFACE ".Station" + #define WPAS_DBUS_NEW_IFACE_P2PDEVICE \ WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice" @@ -163,6 +176,8 @@ int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, u8 bssid[ETH_ALEN], unsigned int id); int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, u8 bssid[ETH_ALEN], unsigned int id); +int wpas_dbus_unregister_sta(struct wpa_supplicant *wpa_s, const u8 *sta); +int wpas_dbus_register_sta(struct wpa_supplicant *wpa_s, const u8 *sta); void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s, const char *name); void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s, @@ -345,6 +360,18 @@ static inline int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, return 0; } +static inline int wpas_dbus_unregister_sta(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + return 0; +} + +static inline int wpas_dbus_register_sta(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + return 0; +} + static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s, const char *name) { diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 94773b329133..6c36d91a0cf8 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -15,6 +15,9 @@ #include "eap_peer/eap_methods.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#include "ap/ap_drv_ops.h" #include "../config.h" #include "../wpa_supplicant_i.h" #include "../driver_i.h" @@ -22,6 +25,7 @@ #include "../bss.h" #include "../scan.h" #include "../autoscan.h" +#include "../ap.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" @@ -3088,6 +3092,27 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason( } +/** + * wpas_dbus_getter_auth_status_code - Get most recent auth status code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "AuthStatusCode" property. + */ +dbus_bool_t wpas_dbus_getter_auth_status_code( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t reason = wpa_s->auth_status_code; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &reason, error); +} + + /** * wpas_dbus_getter_assoc_status_code - Get most recent failed assoc status code * @iter: Pointer to incoming dbus message iter @@ -3109,6 +3134,97 @@ dbus_bool_t wpas_dbus_getter_assoc_status_code( } +/** + * wpas_dbus_getter_roam_time - Get most recent roam time + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "RoamTime" property. + */ +dbus_bool_t wpas_dbus_getter_roam_time( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t roam_time = wpa_s->roam_time.sec * 1000 + + wpa_s->roam_time.usec / 1000; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &roam_time, error); +} + + +/** + * wpas_dbus_getter_roam_complete - Get most recent roam success or failure + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "RoamComplete" property. + */ +dbus_bool_t wpas_dbus_getter_roam_complete( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_bool_t roam_complete = os_reltime_initialized(&wpa_s->roam_time); + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, + &roam_complete, error); +} + + +/** + * wpas_dbus_getter_session_length - Get most recent BSS session length + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "SessionLength" property. + */ +dbus_bool_t wpas_dbus_getter_session_length( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t session_length = wpa_s->session_length.sec * 1000 + + wpa_s->session_length.usec / 1000; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &session_length, error); +} + + +/** + * wpas_dbus_getter_bss_tm_status - Get most BSS Transition Management request + * status code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "BSSTMStatus" property. + */ +dbus_bool_t wpas_dbus_getter_bss_tm_status( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_WNM + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t bss_tm_status = wpa_s->bss_tm_status; +#else /* CONFIG_WNM */ + dbus_uint32_t bss_tm_status = 0; +#endif /* CONFIG_WNM */ + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &bss_tm_status, error); +} + + /** * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age * @iter: Pointer to incoming dbus message iter @@ -3805,6 +3921,320 @@ dbus_bool_t wpas_dbus_setter_iface_global( } +/** + * wpas_dbus_getter_stas - Get connected stations for an interface + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: a list of stations + * + * Getter for "Stations" property. + */ +dbus_bool_t wpas_dbus_getter_stas( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct sta_info *sta = NULL; + char **paths = NULL; + unsigned int i = 0, num = 0; + dbus_bool_t success = FALSE; + + if (!wpa_s->dbus_new_path) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: no D-Bus interface", __func__); + return FALSE; + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + struct hostapd_data *hapd; + + hapd = wpa_s->ap_iface->bss[0]; + sta = hapd->sta_list; + num = hapd->num_sta; + } +#endif /* CONFIG_AP */ + + paths = os_calloc(num, sizeof(char *)); + if (!paths) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + /* Loop through scan results and append each result's object path */ + for (; sta; sta = sta->next) { + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (!paths[i]) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); + goto out; + } + /* Construct the object path for this BSS. */ + os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_STAS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(sta->addr)); + } + + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + paths, num, + error); + +out: + while (i) + os_free(paths[--i]); + os_free(paths); + return success; +} + + +/** + * wpas_dbus_getter_sta_address - Return the address of a connected station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "Address" property. + */ +dbus_bool_t wpas_dbus_getter_sta_address( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + + sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta); + if (!sta) + return FALSE; + + return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + sta->addr, ETH_ALEN, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + +/** + * wpas_dbus_getter_sta_aid - Return the AID of a connected station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "AID" property. + */ +dbus_bool_t wpas_dbus_getter_sta_aid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + + sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta); + if (!sta) + return FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, + &sta->aid, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + +/** + * wpas_dbus_getter_sta_caps - Return the capabilities of a station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "Capabilities" property. + */ +dbus_bool_t wpas_dbus_getter_sta_caps( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + + sta = ap_get_sta(args->wpa_s->ap_iface->bss[0], args->sta); + if (!sta) + return FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16, + &sta->capability, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + +/** + * wpas_dbus_getter_rx_packets - Return the received packets for a station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "RxPackets" property. + */ +dbus_bool_t wpas_dbus_getter_sta_rx_packets( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + struct hostap_sta_driver_data data; + struct hostapd_data *hapd; + + if (!args->wpa_s->ap_iface) + return FALSE; + + hapd = args->wpa_s->ap_iface->bss[0]; + sta = ap_get_sta(hapd, args->sta); + if (!sta) + return FALSE; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64, + &data.rx_packets, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + +/** + * wpas_dbus_getter_tx_packets - Return the transmitted packets for a station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "TxPackets" property. + */ +dbus_bool_t wpas_dbus_getter_sta_tx_packets( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + struct hostap_sta_driver_data data; + struct hostapd_data *hapd; + + if (!args->wpa_s->ap_iface) + return FALSE; + + hapd = args->wpa_s->ap_iface->bss[0]; + sta = ap_get_sta(hapd, args->sta); + if (!sta) + return FALSE; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64, + &data.tx_packets, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + +/** + * wpas_dbus_getter_tx_bytes - Return the transmitted bytes for a station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "TxBytes" property. + */ +dbus_bool_t wpas_dbus_getter_sta_tx_bytes( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + struct hostap_sta_driver_data data; + struct hostapd_data *hapd; + + if (!args->wpa_s->ap_iface) + return FALSE; + + hapd = args->wpa_s->ap_iface->bss[0]; + sta = ap_get_sta(hapd, args->sta); + if (!sta) + return FALSE; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64, + &data.tx_bytes, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + +/** + * wpas_dbus_getter_rx_bytes - Return the received bytes for a station + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "RxBytes" property. + */ +dbus_bool_t wpas_dbus_getter_sta_rx_bytes( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ +#ifdef CONFIG_AP + struct sta_handler_args *args = user_data; + struct sta_info *sta; + struct hostap_sta_driver_data data; + struct hostapd_data *hapd; + + if (!args->wpa_s->ap_iface) + return FALSE; + + hapd = args->wpa_s->ap_iface->bss[0]; + sta = ap_get_sta(hapd, args->sta); + if (!sta) + return FALSE; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return FALSE; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT64, + &data.rx_bytes, + error); +#else /* CONFIG_AP */ + return FALSE; +#endif /* CONFIG_AP */ +} + + static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, DBusError *error, const char *func_name) { @@ -4067,7 +4497,7 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop( DBusMessageIter iter_dict, variant_iter; const char *group; const char *pairwise[5]; /* max 5 pairwise ciphers is supported */ - const char *key_mgmt[13]; /* max 13 key managements may be supported */ + const char *key_mgmt[15]; /* max 15 key managements may be supported */ int n; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, @@ -4077,7 +4507,12 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop( if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) goto nomem; - /* KeyMgmt */ + /* + * KeyMgmt + * + * When adding a new entry here, please take care to extend key_mgmt[] + * and keep documentation in doc/dbus.doxygen up to date. + */ n = 0; if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK) key_mgmt[n++] = "wpa-psk"; @@ -4109,6 +4544,12 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop( if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) key_mgmt[n++] = "wpa-ft-fils-sha384"; #endif /* CONFIG_FILS */ +#ifdef CONFIG_SAE + if (ie_data->key_mgmt & WPA_KEY_MGMT_SAE) + key_mgmt[n++] = "sae"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_SAE) + key_mgmt[n++] = "ft-sae"; +#endif /* CONFIG_SAE */ if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE) key_mgmt[n++] = "wpa-none"; diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index 6f952cc39091..d922ce1b4189 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -22,6 +22,11 @@ struct bss_handler_args { unsigned int id; }; +struct sta_handler_args { + struct wpa_supplicant *wpa_s; + const u8 *sta; +}; + dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter, const int type, const void *val, @@ -145,7 +150,12 @@ DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth); DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth); DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason); DECLARE_ACCESSOR(wpas_dbus_getter_disassociate_reason); +DECLARE_ACCESSOR(wpas_dbus_getter_auth_status_code); DECLARE_ACCESSOR(wpas_dbus_getter_assoc_status_code); +DECLARE_ACCESSOR(wpas_dbus_getter_roam_time); +DECLARE_ACCESSOR(wpas_dbus_getter_roam_complete); +DECLARE_ACCESSOR(wpas_dbus_getter_session_length); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_tm_status); DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age); DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_age); DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_count); @@ -166,6 +176,14 @@ DECLARE_ACCESSOR(wpas_dbus_getter_networks); DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_engine_path); DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path); DECLARE_ACCESSOR(wpas_dbus_getter_blobs); +DECLARE_ACCESSOR(wpas_dbus_getter_stas); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_address); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_aid); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_caps); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_rx_packets); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_tx_packets); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_tx_bytes); +DECLARE_ACCESSOR(wpas_dbus_getter_sta_rx_bytes); DECLARE_ACCESSOR(wpas_dbus_getter_bss_bssid); DECLARE_ACCESSOR(wpas_dbus_getter_bss_ssid); DECLARE_ACCESSOR(wpas_dbus_getter_bss_privacy); diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 9305b9a4f37d..8cdd885644af 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -384,14 +384,14 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, goto inv_args; if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0, - 0, 0, NULL, 0, 0)) { + 0, 0, 0, NULL, 0, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0, - 0)) + 0, 0)) goto inv_args; out: @@ -505,6 +505,7 @@ DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, wpa_s = wpa_s->global->p2p_init_wpa_s; + wpas_p2p_stop_find(wpa_s); os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; p2p_flush(wpa_s->global->p2p); @@ -532,6 +533,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, int new_pin; char *err_msg = NULL; char *iface = NULL; + int ret; if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; @@ -603,13 +605,19 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, - go_intent, freq, 0, -1, 0, 0, 0, 0, NULL, 0); + go_intent, freq, 0, -1, 0, 0, 0, 0, 0, + NULL, 0); if (new_pin >= 0) { char npin[9]; char *generated_pin; - os_snprintf(npin, sizeof(npin), "%08d", new_pin); + ret = os_snprintf(npin, sizeof(npin), "%08d", new_pin); + if (os_snprintf_error(sizeof(npin), ret)) { + reply = wpas_dbus_error_unknown_error(message, + "invalid PIN"); + goto out; + } generated_pin = npin; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_STRING, @@ -755,7 +763,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, goto err; if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0, - 0) < 0) { + 0, 0) < 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -1910,6 +1918,30 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_groups( return success; } +dbus_bool_t wpas_dbus_getter_p2p_peer_vsie( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + + info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, + peer_args->p2p_device_addr, 0); + if (!info) { + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); + return FALSE; + } + + if (!info->vendor_elems) + return wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_BYTE, + NULL, 0, error); + + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_BYTE, (char *) info->vendor_elems->buf, + info->vendor_elems->used, error); +} + /** * wpas_dbus_getter_persistent_groups - Get array of persistent group objects @@ -2661,7 +2693,7 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) goto error; - if (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; @@ -2673,26 +2705,27 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( bonjour = 1; else goto error_clear; - wpa_dbus_dict_entry_clear(&entry); + } else if (os_strcmp(entry.key, "version") == 0 && + entry.type == DBUS_TYPE_INT32) { + version = entry.uint32_value; + } else if (os_strcmp(entry.key, "service") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(service); + service = os_strdup(entry.str_value); + } else if (os_strcmp(entry.key, "query") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) + goto error_clear; + wpabuf_free(query); + query = wpabuf_alloc_copy(entry.bytearray_value, + entry.array_len); + } else { + goto error_clear; } + + wpa_dbus_dict_entry_clear(&entry); } if (upnp == 1) { - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - if (os_strcmp(entry.key, "version") == 0 && - entry.type == DBUS_TYPE_INT32) - version = entry.uint32_value; - else if (os_strcmp(entry.key, "service") == 0 && - entry.type == DBUS_TYPE_STRING) { - os_free(service); - service = os_strdup(entry.str_value); - } else - goto error_clear; - - wpa_dbus_dict_entry_clear(&entry); - } - if (version <= 0 || service == NULL) goto error; @@ -2700,24 +2733,6 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( if (ret != 0) goto error; } else if (bonjour == 1) { - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - - if (os_strcmp(entry.key, "query") == 0) { - if (entry.type != DBUS_TYPE_ARRAY || - entry.array_type != DBUS_TYPE_BYTE) - goto error_clear; - wpabuf_free(query); - query = wpabuf_alloc_copy( - entry.bytearray_value, - entry.array_len); - } else - goto error_clear; - - wpa_dbus_dict_entry_clear(&entry); - } - if (query == NULL) goto error; diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index c4c02615dbc3..b3c45c11012c 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -114,6 +114,7 @@ DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vendor_extension); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_ies); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_address); DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_groups); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vsie); /* * P2P Group properties diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index f762b3f2ef5c..1594dafc7bb5 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -286,10 +286,14 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin, 0, DEV_PW_DEFAULT); - if (ret > 0) - os_snprintf(npin, sizeof(npin), "%08d", ret); + if (ret > 0) { + ret = os_snprintf(npin, sizeof(npin), "%08d", ret); + if (os_snprintf_error(sizeof(npin), ret)) + return wpas_dbus_error_unknown_error( + message, "invalid PIN"); + } } else { - ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0); + ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0, 0); } if (ret < 0) { diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c deleted file mode 100644 index 88227af7c03b..000000000000 --- a/wpa_supplicant/dbus/dbus_old.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams and Red Hat, 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 "eloop.h" -#include "wps/wps.h" -#include "../config.h" -#include "../wpa_supplicant_i.h" -#include "../bss.h" -#include "dbus_old.h" -#include "dbus_old_handlers.h" -#include "dbus_common_i.h" - - -/** - * wpas_dbus_decompose_object_path - Decompose an interface object path into parts - * @path: The dbus object path - * @network: (out) the configured network this object path refers to, if any - * @bssid: (out) the scanned bssid this object path refers to, if any - * Returns: The object path of the network interface this path refers to - * - * For a given object path, decomposes the object path into object id, network, - * and BSSID parts, if those parts exist. - */ -char * wpas_dbus_decompose_object_path(const char *path, char **network, - char **bssid) -{ - const unsigned int dev_path_prefix_len = - strlen(WPAS_DBUS_PATH_INTERFACES "/"); - char *obj_path_only; - char *next_sep; - - /* Be a bit paranoid about path */ - if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/", - dev_path_prefix_len)) - return NULL; - - /* Ensure there's something at the end of the path */ - if ((path + dev_path_prefix_len)[0] == '\0') - return NULL; - - obj_path_only = os_strdup(path); - if (obj_path_only == NULL) - return NULL; - - next_sep = strchr(obj_path_only + dev_path_prefix_len, '/'); - if (next_sep != NULL) { - const char *net_part = strstr(next_sep, - WPAS_DBUS_NETWORKS_PART "/"); - const char *bssid_part = strstr(next_sep, - WPAS_DBUS_BSSIDS_PART "/"); - - if (network && net_part) { - /* Deal with a request for a configured network */ - const char *net_name = net_part + - strlen(WPAS_DBUS_NETWORKS_PART "/"); - *network = NULL; - if (strlen(net_name)) - *network = os_strdup(net_name); - } else if (bssid && bssid_part) { - /* Deal with a request for a scanned BSSID */ - const char *bssid_name = bssid_part + - strlen(WPAS_DBUS_BSSIDS_PART "/"); - if (strlen(bssid_name)) - *bssid = os_strdup(bssid_name); - else - *bssid = NULL; - } - - /* Cut off interface object path before "/" */ - *next_sep = '\0'; - } - - return obj_path_only; -} - - -/** - * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: A dbus error message - * - * Convenience function to create and return an invalid interface error - */ -DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) -{ - return dbus_message_new_error( - message, WPAS_ERROR_INVALID_IFACE, - "wpa_supplicant knows nothing about this interface."); -} - - -/** - * wpas_dbus_new_invalid_network_error - Return a new invalid network error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: a dbus error message - * - * Convenience function to create and return an invalid network error - */ -DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message) -{ - return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK, - "The requested network does not exist."); -} - - -/** - * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: a dbus error message - * - * Convenience function to create and return an invalid bssid error - */ -static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message) -{ - return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID, - "The BSSID requested was invalid."); -} - - -/** - * wpas_dispatch_network_method - dispatch messages for configured networks - * @message: the incoming dbus message - * @wpa_s: a network interface's data - * @network_id: id of the configured network we're interested in - * Returns: a reply dbus message, or a dbus error message - * - * This function dispatches all incoming dbus messages for configured networks. - */ -static DBusMessage * wpas_dispatch_network_method(DBusMessage *message, - struct wpa_supplicant *wpa_s, - int network_id) -{ - DBusMessage *reply = NULL; - const char *method = dbus_message_get_member(message); - struct wpa_ssid *ssid; - - ssid = wpa_config_get_network(wpa_s->conf, network_id); - if (ssid == NULL) - return wpas_dbus_new_invalid_network_error(message); - - if (!strcmp(method, "set")) - reply = wpas_dbus_iface_set_network(message, wpa_s, ssid); - else if (!strcmp(method, "enable")) - reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid); - else if (!strcmp(method, "disable")) - reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid); - - return reply; -} - - -/** - * wpas_dispatch_bssid_method - dispatch messages for scanned networks - * @message: the incoming dbus message - * @wpa_s: a network interface's data - * @bssid: bssid of the scanned network we're interested in - * Returns: a reply dbus message, or a dbus error message - * - * This function dispatches all incoming dbus messages for scanned networks. - */ -static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message, - struct wpa_supplicant *wpa_s, - const char *bssid_txt) -{ - u8 bssid[ETH_ALEN]; - struct wpa_bss *bss; - - if (hexstr2bin(bssid_txt, bssid, ETH_ALEN) < 0) - return wpas_dbus_new_invalid_bssid_error(message); - - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (bss == NULL) - return wpas_dbus_new_invalid_bssid_error(message); - - /* Dispatch the method call against the scanned bssid */ - if (os_strcmp(dbus_message_get_member(message), "properties") == 0) - return wpas_dbus_bssid_properties(message, wpa_s, bss); - - return NULL; -} - - -/** - * wpas_iface_message_handler - Dispatch messages for interfaces or networks - * @connection: Connection to the system message bus - * @message: An incoming dbus message - * @user_data: A pointer to a dbus control interface data structure - * Returns: Whether or not the message was handled - * - * This function dispatches all incoming dbus messages for network interfaces, - * or objects owned by them, such as scanned BSSIDs and configured networks. - */ -static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, - DBusMessage *message, - void *user_data) -{ - struct wpa_supplicant *wpa_s = user_data; - const char *method = dbus_message_get_member(message); - const char *path = dbus_message_get_path(message); - const char *msg_interface = dbus_message_get_interface(message); - char *iface_obj_path = NULL; - char *network = NULL; - char *bssid = NULL; - DBusMessage *reply = NULL; - - /* Caller must specify a message interface */ - if (!msg_interface) - goto out; - - wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]", - msg_interface, method, path, - dbus_message_get_signature(message)); - - iface_obj_path = wpas_dbus_decompose_object_path(path, &network, - &bssid); - if (iface_obj_path == NULL) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - /* Make sure the message's object path actually refers to the - * wpa_supplicant structure it's supposed to (which is wpa_s) - */ - if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, - iface_obj_path) != wpa_s) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { - /* A method for one of this interface's configured networks */ - int nid = strtoul(network, NULL, 10); - - if (errno != EINVAL) - reply = wpas_dispatch_network_method(message, wpa_s, - nid); - else - reply = wpas_dbus_new_invalid_network_error(message); - } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) { - /* A method for one of this interface's scanned BSSIDs */ - reply = wpas_dispatch_bssid_method(message, wpa_s, bssid); - } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) { - /* A method for an interface only. */ - if (!strcmp(method, "scan")) - reply = wpas_dbus_iface_scan(message, wpa_s); - else if (!strcmp(method, "scanResults")) - reply = wpas_dbus_iface_scan_results(message, wpa_s); - else if (!strcmp(method, "addNetwork")) - reply = wpas_dbus_iface_add_network(message, wpa_s); - else if (!strcmp(method, "removeNetwork")) - reply = wpas_dbus_iface_remove_network(message, wpa_s); - else if (!strcmp(method, "selectNetwork")) - reply = wpas_dbus_iface_select_network(message, wpa_s); - else if (!strcmp(method, "capabilities")) - reply = wpas_dbus_iface_capabilities(message, wpa_s); - else if (!strcmp(method, "disconnect")) - reply = wpas_dbus_iface_disconnect(message, wpa_s); - else if (!strcmp(method, "setAPScan")) - reply = wpas_dbus_iface_set_ap_scan(message, wpa_s); - else if (!strcmp(method, "setSmartcardModules")) - reply = wpas_dbus_iface_set_smartcard_modules(message, - wpa_s); - else if (!strcmp(method, "state")) - reply = wpas_dbus_iface_get_state(message, wpa_s); - else if (!strcmp(method, "scanning")) - reply = wpas_dbus_iface_get_scanning(message, wpa_s); -#ifndef CONFIG_NO_CONFIG_BLOBS - else if (!strcmp(method, "setBlobs")) - reply = wpas_dbus_iface_set_blobs(message, wpa_s); - else if (!strcmp(method, "removeBlobs")) - reply = wpas_dbus_iface_remove_blobs(message, wpa_s); -#endif /* CONFIG_NO_CONFIG_BLOBS */ -#ifdef CONFIG_WPS - else if (os_strcmp(method, "wpsPbc") == 0) - reply = wpas_dbus_iface_wps_pbc(message, wpa_s); - else if (os_strcmp(method, "wpsPin") == 0) - reply = wpas_dbus_iface_wps_pin(message, wpa_s); - else if (os_strcmp(method, "wpsReg") == 0) - reply = wpas_dbus_iface_wps_reg(message, wpa_s); -#endif /* CONFIG_WPS */ - else if (os_strcmp(method, "flush") == 0) - reply = wpas_dbus_iface_flush(message, wpa_s); - } - - /* If the message was handled, send back the reply */ -out: - if (reply) { - if (!dbus_message_get_no_reply(message)) - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } - - os_free(iface_obj_path); - os_free(network); - os_free(bssid); - return reply ? DBUS_HANDLER_RESULT_HANDLED : - DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - - -/** - * wpas_message_handler - dispatch incoming dbus messages - * @connection: connection to the system message bus - * @message: an incoming dbus message - * @user_data: a pointer to a dbus control interface data structure - * Returns: whether or not the message was handled - * - * This function dispatches all incoming dbus messages to the correct - * handlers, depending on what the message's target object path is, - * and what the method call is. - */ -static DBusHandlerResult wpas_message_handler(DBusConnection *connection, - DBusMessage *message, void *user_data) -{ - struct wpas_dbus_priv *ctrl_iface = user_data; - const char *method; - const char *path; - const char *msg_interface; - DBusMessage *reply = NULL; - - method = dbus_message_get_member(message); - path = dbus_message_get_path(message); - msg_interface = dbus_message_get_interface(message); - if (!method || !path || !ctrl_iface || !msg_interface) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]", - msg_interface, method, path, - dbus_message_get_signature(message)); - - /* Validate the method interface */ - if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (!strcmp(path, WPAS_DBUS_PATH)) { - /* dispatch methods against our global dbus interface here */ - if (!strcmp(method, "addInterface")) { - reply = wpas_dbus_global_add_interface( - message, ctrl_iface->global); - } else if (!strcmp(method, "removeInterface")) { - reply = wpas_dbus_global_remove_interface( - message, ctrl_iface->global); - } else if (!strcmp(method, "getInterface")) { - reply = wpas_dbus_global_get_interface( - message, ctrl_iface->global); - } else if (!strcmp(method, "setDebugParams")) { - reply = wpas_dbus_global_set_debugparams( - message, ctrl_iface->global); - } - } - - /* If the message was handled, send back the reply */ - if (reply) { - if (!dbus_message_get_no_reply(message)) - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } - - return reply ? DBUS_HANDLER_RESULT_HANDLED : - DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - - -/** - * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal - * @wpa_s: %wpa_supplicant network interface data - * Returns: 0 on success, -1 on failure - * - * Notify listeners that this interface has updated scan results. - */ -void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *iface = wpa_s->global->dbus; - DBusMessage *_signal; - - /* Do nothing if the control interface is not turned on */ - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "ScanResultsAvailable"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to send scan results signal"); - return; - } - dbus_connection_send(iface->con, _signal, NULL); - dbus_message_unref(_signal); -} - - -/** - * wpa_supplicant_dbus_notify_state_change - Send a state change signal - * @wpa_s: %wpa_supplicant network interface data - * @new_state: new state wpa_supplicant is entering - * @old_state: old state wpa_supplicant is leaving - * Returns: 0 on success, -1 on failure - * - * Notify listeners that wpa_supplicant has changed state - */ -void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, - enum wpa_states new_state, - enum wpa_states old_state) -{ - struct wpas_dbus_priv *iface; - DBusMessage *_signal = NULL; - const char *new_state_str, *old_state_str; - - if (wpa_s->dbus_path == NULL) - return; /* Skip signal since D-Bus setup is not yet ready */ - - /* Do nothing if the control interface is not turned on */ - if (wpa_s->global == NULL) - return; - iface = wpa_s->global->dbus; - if (iface == NULL) - return; - - /* Only send signal if state really changed */ - if (new_state == old_state) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "StateChange"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: %s: could not create dbus signal; likely out of memory", - __func__); - return; - } - - new_state_str = wpa_supplicant_state_txt(new_state); - old_state_str = wpa_supplicant_state_txt(old_state); - - if (!dbus_message_append_args(_signal, - DBUS_TYPE_STRING, &new_state_str, - DBUS_TYPE_STRING, &old_state_str, - DBUS_TYPE_INVALID)) { - wpa_printf(MSG_ERROR, - "dbus: %s: Not enough memory to construct state change signal", - __func__); - goto out; - } - - dbus_connection_send(iface->con, _signal, NULL); - -out: - dbus_message_unref(_signal); -} - - -/** - * wpa_supplicant_dbus_notify_scanning - send scanning status - * @wpa_s: %wpa_supplicant network interface data - * Returns: 0 on success, -1 on failure - * - * Notify listeners of interface scanning state changes - */ -void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *iface = wpa_s->global->dbus; - DBusMessage *_signal; - dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; - - /* Do nothing if the control interface is not turned on */ - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "Scanning"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to send scan results signal"); - return; - } - - if (dbus_message_append_args(_signal, - DBUS_TYPE_BOOLEAN, &scanning, - DBUS_TYPE_INVALID)) { - dbus_connection_send(iface->con, _signal, NULL); - } else { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to construct signal"); - } - dbus_message_unref(_signal); -} - - -#ifdef CONFIG_WPS -void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred) -{ - struct wpas_dbus_priv *iface; - DBusMessage *_signal = NULL; - - /* Do nothing if the control interface is not turned on */ - if (wpa_s->global == NULL) - return; - iface = wpa_s->global->dbus; - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "WpsCred"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: %s: Could not create dbus signal; likely out of memory", - __func__); - return; - } - - if (!dbus_message_append_args(_signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &cred->cred_attr, cred->cred_attr_len, - DBUS_TYPE_INVALID)) { - wpa_printf(MSG_ERROR, - "dbus: %s: Not enough memory to construct signal", - __func__); - goto out; - } - - dbus_connection_send(iface->con, _signal, NULL); - -out: - dbus_message_unref(_signal); -} -#else /* CONFIG_WPS */ -void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred) -{ -} -#endif /* CONFIG_WPS */ - -void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, - int depth, const char *subject, - const char *cert_hash, - const struct wpabuf *cert) -{ - struct wpas_dbus_priv *iface; - DBusMessage *_signal = NULL; - const char *hash; - const char *cert_hex; - int cert_hex_len; - - /* Do nothing if the control interface is not turned on */ - if (wpa_s->global == NULL) - return; - iface = wpa_s->global->dbus; - if (iface == NULL || !wpa_s->dbus_path) - return; - - _signal = dbus_message_new_signal(wpa_s->dbus_path, - WPAS_DBUS_IFACE_INTERFACE, - "Certification"); - if (_signal == NULL) { - wpa_printf(MSG_ERROR, - "dbus: %s: Could not create dbus signal; likely out of memory", - __func__); - return; - } - - hash = cert_hash ? cert_hash : ""; - cert_hex = cert ? wpabuf_head(cert) : ""; - cert_hex_len = cert ? wpabuf_len(cert) : 0; - - if (!dbus_message_append_args(_signal, - DBUS_TYPE_INT32, &depth, - DBUS_TYPE_STRING, &subject, - DBUS_TYPE_STRING, &hash, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &cert_hex, cert_hex_len, - DBUS_TYPE_INVALID)) { - wpa_printf(MSG_ERROR, - "dbus: %s: Not enough memory to construct signal", - __func__); - goto out; - } - - dbus_connection_send(iface->con, _signal, NULL); - -out: - dbus_message_unref(_signal); - -} - - -/** - * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface - * @global: Pointer to global data from wpa_supplicant_init() - * Returns: 0 on success, -1 on failure - * - * Initialize the dbus control interface and start receiving commands from - * external programs over the bus. - */ -int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) -{ - DBusError error; - int ret = -1; - DBusObjectPathVTable wpas_vtable = { - NULL, &wpas_message_handler, NULL, NULL, NULL, NULL - }; - - /* Register the message handler for the global dbus interface */ - if (!dbus_connection_register_object_path(iface->con, - WPAS_DBUS_PATH, &wpas_vtable, - iface)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message handler"); - return -1; - } - - /* Register our service with the message bus */ - dbus_error_init(&error); - switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE, - 0, &error)) { - case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: - ret = 0; - break; - case DBUS_REQUEST_NAME_REPLY_EXISTS: - case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: - case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - wpa_printf(MSG_ERROR, - "dbus: Could not request service name: already registered"); - break; - default: - wpa_printf(MSG_ERROR, - "dbus: Could not request service name: %s %s", - error.name, error.message); - break; - } - dbus_error_free(&error); - - if (ret != 0) - return -1; - - wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE - "'."); - - return 0; -} - - -/** - * wpas_dbus_register_new_iface - Register a new interface with dbus - * @wpa_s: %wpa_supplicant interface description structure to register - * Returns: 0 on success, -1 on error - * - * Registers a new interface with dbus and assigns it a dbus object path. - */ -int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; - DBusConnection * con; - u32 next; - DBusObjectPathVTable vtable = { - NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL - }; - - /* Do nothing if the control interface is not turned on */ - if (ctrl_iface == NULL) - return 0; - - con = ctrl_iface->con; - next = ctrl_iface->next_objid++; - - /* Create and set the interface's object path */ - wpa_s->dbus_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); - if (wpa_s->dbus_path == NULL) - return -1; - os_snprintf(wpa_s->dbus_path, WPAS_DBUS_OBJECT_PATH_MAX, - WPAS_DBUS_PATH_INTERFACES "/%u", - next); - - /* Register the message handler for the interface functions */ - if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable, - wpa_s)) { - wpa_printf(MSG_ERROR, - "dbus: Could not set up message handler for interface %s", - wpa_s->ifname); - return -1; - } - - return 0; -} - - -/** - * wpas_dbus_unregister_iface - Unregister an interface from dbus - * @wpa_s: wpa_supplicant interface structure - * Returns: 0 on success, -1 on failure - * - * Unregisters the interface with dbus - */ -int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) -{ - struct wpas_dbus_priv *ctrl_iface; - DBusConnection *con; - - /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL) - return 0; - ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL || wpa_s->dbus_path == NULL) - return 0; - - con = ctrl_iface->con; - if (!dbus_connection_unregister_object_path(con, wpa_s->dbus_path)) - return -1; - - os_free(wpa_s->dbus_path); - wpa_s->dbus_path = NULL; - - return 0; -} - - -/** - * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface - * @global: Pointer to global data from wpa_supplicant_init() - * @path: Pointer to a dbus object path representing an interface - * Returns: Pointer to the interface or %NULL if not found - */ -struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( - struct wpa_global *global, const char *path) -{ - struct wpa_supplicant *wpa_s; - - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0) - return wpa_s; - } - return NULL; -} diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h deleted file mode 100644 index 451a9f827aa9..000000000000 --- a/wpa_supplicant/dbus/dbus_old.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef CTRL_IFACE_DBUS_H -#define CTRL_IFACE_DBUS_H - -struct wps_credential; - -#ifdef CONFIG_CTRL_IFACE_DBUS - -#define WPAS_DBUS_OBJECT_PATH_MAX 150 - -#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant" -#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant" -#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant" - -#define WPAS_DBUS_PATH_INTERFACES WPAS_DBUS_PATH "/Interfaces" -#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" - -#define WPAS_DBUS_NETWORKS_PART "Networks" -#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" - -#define WPAS_DBUS_BSSIDS_PART "BSSIDs" -#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID" - - -/* Errors */ -#define WPAS_ERROR_INVALID_NETWORK \ - WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork" -#define WPAS_ERROR_INVALID_BSSID \ - WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID" - -#define WPAS_ERROR_INVALID_OPTS \ - WPAS_DBUS_INTERFACE ".InvalidOptions" -#define WPAS_ERROR_INVALID_IFACE \ - WPAS_DBUS_INTERFACE ".InvalidInterface" - -#define WPAS_ERROR_ADD_ERROR \ - WPAS_DBUS_INTERFACE ".AddError" -#define WPAS_ERROR_EXISTS_ERROR \ - WPAS_DBUS_INTERFACE ".ExistsError" -#define WPAS_ERROR_REMOVE_ERROR \ - WPAS_DBUS_INTERFACE ".RemoveError" - -#define WPAS_ERROR_SCAN_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".ScanError" -#define WPAS_ERROR_ADD_NETWORK_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError" -#define WPAS_ERROR_INTERNAL_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".InternalError" -#define WPAS_ERROR_REMOVE_NETWORK_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError" - -#define WPAS_ERROR_WPS_PBC_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".WpsPbcError" -#define WPAS_ERROR_WPS_PIN_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".WpsPinError" -#define WPAS_ERROR_WPS_REG_ERROR \ - WPAS_DBUS_IFACE_INTERFACE ".WpsRegError" - -#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x" - -struct wpa_global; -struct wpa_supplicant; - -int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface); -void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s); -void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s); -void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, - enum wpa_states new_state, - enum wpa_states old_state); -void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred); -void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, - int depth, const char *subject, - const char *cert_hash, - const struct wpabuf *cert); - -char * wpas_dbus_decompose_object_path(const char *path, char **network, - char **bssid); - -int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s); -int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s); - - -/* Methods internal to the dbus control interface */ -struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( - struct wpa_global *global, const char *path); - -#else /* CONFIG_CTRL_IFACE_DBUS */ - -static inline void -wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) -{ -} - -static inline void -wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) -{ -} - -static inline void -wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, - enum wpa_states new_state, - enum wpa_states old_state) -{ -} - -static inline void -wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, - const struct wps_credential *cred) -{ -} - -static inline void -wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, - int depth, const char *subject, - const char *cert_hash, - const struct wpabuf *cert) -{ -} - -static inline int -wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) -{ - return 0; -} - -static inline int -wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) -{ - return 0; -} - -#endif /* CONFIG_CTRL_IFACE_DBUS */ - -#endif /* CTRL_IFACE_DBUS_H */ diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c deleted file mode 100644 index e540832f254b..000000000000 --- a/wpa_supplicant/dbus/dbus_old_handlers.c +++ /dev/null @@ -1,1393 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams and Red Hat, 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 "eap_peer/eap_methods.h" -#include "common/ieee802_11_defs.h" -#include "eapol_supp/eapol_supp_sm.h" -#include "rsn_supp/wpa.h" -#include "../config.h" -#include "../wpa_supplicant_i.h" -#include "../driver_i.h" -#include "../notify.h" -#include "../wpas_glue.h" -#include "../bss.h" -#include "../scan.h" -#include "dbus_old.h" -#include "dbus_old_handlers.h" -#include "dbus_dict_helpers.h" - -/** - * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message - * @message: Pointer to incoming dbus message this error refers to - * Returns: a dbus error message - * - * Convenience function to create and return an invalid options error - */ -DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, - const char *arg) -{ - DBusMessage *reply; - - reply = dbus_message_new_error( - message, WPAS_ERROR_INVALID_OPTS, - "Did not receive correct message arguments."); - if (arg != NULL) - dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, - DBUS_TYPE_INVALID); - - return reply; -} - - -/** - * wpas_dbus_new_success_reply - Return a new success reply message - * @message: Pointer to incoming dbus message this reply refers to - * Returns: a dbus message containing a single UINT32 that indicates - * success (ie, a value of 1) - * - * Convenience function to create and return a success reply message - */ -DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message) -{ - DBusMessage *reply; - unsigned int success = 1; - - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success, - DBUS_TYPE_INVALID); - return reply; -} - - -/** - * wpas_dbus_global_add_interface - Request registration of a network interface - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: The object path of the new interface object, - * or a dbus error message with more information - * - * Handler function for "addInterface" method call. Handles requests - * by dbus clients to register a network interface that wpa_supplicant - * will manage. - */ -DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, - struct wpa_global *global) -{ - char *ifname = NULL; - char *driver = NULL; - char *driver_param = NULL; - char *confname = NULL; - char *bridge_ifname = NULL; - DBusMessage *reply = NULL; - DBusMessageIter iter; - - dbus_message_iter_init(message, &iter); - - /* First argument: interface name (DBUS_TYPE_STRING) - * Required; must be non-zero length - */ - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - goto error; - dbus_message_iter_get_basic(&iter, &ifname); - if (!os_strlen(ifname)) - goto error; - - /* Second argument: dict of options */ - if (dbus_message_iter_next(&iter)) { - DBusMessageIter iter_dict; - struct wpa_dbus_dict_entry entry; - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) - goto error; - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - if (!strcmp(entry.key, "driver") && - entry.type == DBUS_TYPE_STRING) { - os_free(driver); - driver = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (driver == NULL) - goto error; - } else if (!strcmp(entry.key, "driver-params") && - entry.type == DBUS_TYPE_STRING) { - os_free(driver_param); - driver_param = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (driver_param == NULL) - goto error; - } else if (!strcmp(entry.key, "config-file") && - entry.type == DBUS_TYPE_STRING) { - os_free(confname); - confname = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (confname == NULL) - goto error; - } else if (!strcmp(entry.key, "bridge-ifname") && - entry.type == DBUS_TYPE_STRING) { - os_free(bridge_ifname); - bridge_ifname = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (bridge_ifname == NULL) - goto error; - } else { - wpa_dbus_dict_entry_clear(&entry); - goto error; - } - } - } - - /* - * Try to get the wpa_supplicant record for this iface, return - * an error if we already control it. - */ - if (wpa_supplicant_get_iface(global, ifname) != NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_EXISTS_ERROR, - "wpa_supplicant already controls this interface."); - } else { - struct wpa_supplicant *wpa_s; - struct wpa_interface iface; - - os_memset(&iface, 0, sizeof(iface)); - iface.ifname = ifname; - iface.driver = driver; - iface.driver_param = driver_param; - iface.confname = confname; - iface.bridge_ifname = bridge_ifname; - /* Otherwise, have wpa_supplicant attach to it. */ - wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); - if (wpa_s && wpa_s->dbus_path) { - const char *path = wpa_s->dbus_path; - - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); - } else { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "wpa_supplicant couldn't grab this interface."); - } - } - -out: - os_free(driver); - os_free(driver_param); - os_free(confname); - os_free(bridge_ifname); - return reply; - -error: - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; -} - - -/** - * wpas_dbus_global_remove_interface - Request deregistration of an interface - * @message: Pointer to incoming dbus message - * @global: wpa_supplicant global data structure - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0), or returns a dbus error message with more information - * - * Handler function for "removeInterface" method call. Handles requests - * by dbus clients to deregister a network interface that wpa_supplicant - * currently manages. - */ -DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, - struct wpa_global *global) -{ - struct wpa_supplicant *wpa_s; - char *path; - DBusMessage *reply = NULL; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path); - if (wpa_s == NULL) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { - reply = wpas_dbus_new_success_reply(message); - } else { - reply = dbus_message_new_error( - message, WPAS_ERROR_REMOVE_ERROR, - "wpa_supplicant couldn't remove this interface."); - } - -out: - return reply; -} - - -/** - * wpas_dbus_global_get_interface - Get the object path for an interface name - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: The object path of the interface object, - * or a dbus error message with more information - * - * Handler function for "getInterface" method call. Handles requests - * by dbus clients for the object path of an specific network interface. - */ -DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, - struct wpa_global *global) -{ - DBusMessage *reply = NULL; - const char *ifname; - const char *path; - struct wpa_supplicant *wpa_s; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &ifname, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - wpa_s = wpa_supplicant_get_iface(global, ifname); - if (wpa_s == NULL || !wpa_s->dbus_path) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - - path = wpa_s->dbus_path; - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - -out: - return reply; -} - - -/** - * wpas_dbus_global_set_debugparams- Set the debug params - * @message: Pointer to incoming dbus message - * @global: %wpa_supplicant global data structure - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0), or returns a dbus error message with more information - * - * Handler function for "setDebugParams" method call. Handles requests - * by dbus clients for the object path of an specific network interface. - */ -DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, - struct wpa_global *global) -{ - DBusMessage *reply = NULL; - int debug_level; - dbus_bool_t debug_timestamp; - dbus_bool_t debug_show_keys; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_INT32, &debug_level, - DBUS_TYPE_BOOLEAN, &debug_timestamp, - DBUS_TYPE_BOOLEAN, &debug_show_keys, - DBUS_TYPE_INVALID)) { - return wpas_dbus_new_invalid_opts_error(message, NULL); - } - - if (wpa_supplicant_set_debug_params(global, debug_level, - debug_timestamp ? 1 : 0, - debug_show_keys ? 1 : 0)) { - return wpas_dbus_new_invalid_opts_error(message, NULL); - } - - reply = wpas_dbus_new_success_reply(message); - - return reply; -} - - -/** - * wpas_dbus_iface_scan - Request a wireless scan on an interface - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "scan" method call of a network device. Requests - * that wpa_supplicant perform a wireless scan as soon as possible - * on a particular wireless interface. - */ -DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_supplicant_req_scan(wpa_s, 0, 0); - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_scan_results - Get the results of a recent scan request - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: a dbus message containing a dbus array of objects paths, or returns - * a dbus error message if not scan results could be found - * - * Handler function for "scanResults" method call of a network device. Returns - * a dbus message containing the object paths of wireless networks found. - */ -DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub_iter; - struct wpa_bss *bss; - - if (!wpa_s->dbus_path) - return dbus_message_new_error(message, - WPAS_ERROR_INTERNAL_ERROR, - "no D-Bus interface available"); - - /* Create and initialize the return message */ - reply = dbus_message_new_method_return(message); - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_OBJECT_PATH_AS_STRING, - &sub_iter)) - goto error; - - /* Loop through scan results and append each result's object path */ - dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { - char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; - char *path = path_buf; - - /* Construct the object path for this network. Note that ':' - * is not a valid character in dbus object paths. - */ - os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_BSSIDS_PART "/" - WPAS_DBUS_BSSID_FORMAT, - wpa_s->dbus_path, MAC2STR(bss->bssid)); - if (!dbus_message_iter_append_basic(&sub_iter, - DBUS_TYPE_OBJECT_PATH, - &path)) - goto error; - } - - if (!dbus_message_iter_close_container(&iter, &sub_iter)) - goto error; - - return reply; - -error: - dbus_message_unref(reply); - return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning scan results"); -} - - -/** - * wpas_dbus_bssid_properties - Return the properties of a scanned network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @res: wpa_supplicant scan result for which to get properties - * Returns: a dbus message containing the properties for the requested network - * - * Handler function for "properties" method call of a scanned network. - * Returns a dbus message containing the the properties. - */ -DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) -{ - DBusMessage *reply; - DBusMessageIter iter, iter_dict; - const u8 *wpa_ie, *rsn_ie, *wps_ie; - - /* Dump the properties into a dbus message */ - reply = dbus_message_new_method_return(message); - - wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); - wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - - dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict) || - !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", - (const char *) bss->bssid, - ETH_ALEN) || - !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", - (const char *) bss->ssid, - bss->ssid_len) || - (wpa_ie && - !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", - (const char *) wpa_ie, - wpa_ie[1] + 2)) || - (rsn_ie && - !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", - (const char *) rsn_ie, - rsn_ie[1] + 2)) || - (wps_ie && - !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", - (const char *) wps_ie, - wps_ie[1] + 2)) || - (bss->freq && - !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) || - !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", - bss->caps) || - (!(bss->flags & WPA_BSS_QUAL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) || - (!(bss->flags & WPA_BSS_NOISE_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) || - (!(bss->flags & WPA_BSS_LEVEL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) || - !wpa_dbus_dict_append_int32(&iter_dict, "maxrate", - wpa_bss_get_max_rate(bss) * 500000) || - !wpa_dbus_dict_close_write(&iter, &iter_dict)) { - if (reply) - dbus_message_unref(reply); - reply = dbus_message_new_error( - message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning BSSID properties."); - } - - return reply; -} - - -/** - * wpas_dbus_iface_capabilities - Return interface capabilities - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a dict of strings - * - * Handler function for "capabilities" method call of an interface. - */ -DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - struct wpa_driver_capa capa; - int res; - DBusMessageIter iter, iter_dict; - char **eap_methods; - size_t num_items; - dbus_bool_t strict = FALSE; - DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_BOOLEAN, &strict, - DBUS_TYPE_INVALID)) - strict = FALSE; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto error; - - /* EAP methods */ - eap_methods = eap_get_names_as_string_array(&num_items); - if (eap_methods) { - dbus_bool_t success; - size_t i = 0; - - success = wpa_dbus_dict_append_string_array( - &iter_dict, "eap", (const char **) eap_methods, - num_items); - - /* free returned method array */ - while (eap_methods[i]) - os_free(eap_methods[i++]); - os_free(eap_methods); - - if (!success) - goto error; - } - - res = wpa_drv_get_capa(wpa_s, &capa); - - /***** pairwise cipher */ - if (res < 0) { - if (!strict) { - const char *args[] = {"CCMP", "TKIP", "NONE"}; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "pairwise", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) || - ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "NONE")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** group cipher */ - if (res < 0) { - if (!strict) { - const char *args[] = { - "CCMP", "TKIP", "WEP104", "WEP40" - }; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "group", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group", - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - - if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP104")) || - ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP40")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** key management */ - if (res < 0) { - if (!strict) { - const char *args[] = { - "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE", - "NONE" - }; - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "key_mgmt", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - !wpa_dbus_dict_string_array_add_element(&iter_array, - "NONE") || - !wpa_dbus_dict_string_array_add_element(&iter_array, - "IEEE8021X") || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-EAP")) || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-PSK")) || - ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-NONE")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** WPA protocol */ - if (res < 0) { - if (!strict) { - const char *args[] = { "RSN", "WPA" }; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "proto", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "RSN")) || - ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - /***** auth alg */ - if (res < 0) { - if (!strict) { - const char *args[] = { "OPEN", "SHARED", "LEAP" }; - - if (!wpa_dbus_dict_append_string_array( - &iter_dict, "auth_alg", args, - ARRAY_SIZE(args))) - goto error; - } - } else { - if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", - &iter_dict_entry, - &iter_dict_val, - &iter_array) || - ((capa.auth & WPA_DRIVER_AUTH_OPEN) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "OPEN")) || - ((capa.auth & WPA_DRIVER_AUTH_SHARED) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "SHARED")) || - ((capa.auth & WPA_DRIVER_AUTH_LEAP) && - !wpa_dbus_dict_string_array_add_element( - &iter_array, "LEAP")) || - !wpa_dbus_dict_end_string_array(&iter_dict, - &iter_dict_entry, - &iter_dict_val, - &iter_array)) - goto error; - } - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) - goto error; - - return reply; - -error: - if (reply) - dbus_message_unref(reply); - return dbus_message_new_error( - message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning interface capabilities."); -} - - -/** - * wpas_dbus_iface_add_network - Add a new configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing the object path of the new network - * - * Handler function for "addNetwork" method call of a network interface. - */ -DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - struct wpa_ssid *ssid = NULL; - char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; - - if (wpa_s->dbus_path) - ssid = wpa_supplicant_add_network(wpa_s); - if (ssid == NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_NETWORK_ERROR, - "wpa_supplicant could not add a network on this interface."); - goto out; - } - - /* Construct the object path for this network. */ - os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NETWORKS_PART "/%d", - wpa_s->dbus_path, ssid->id); - - reply = dbus_message_new_method_return(message); - dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); - -out: - return reply; -} - - -/** - * wpas_dbus_iface_remove_network - Remove a configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "removeNetwork" method call of a network interface. - */ -DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - const char *op; - char *iface = NULL, *net_id = NULL; - int id; - int result; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - /* Extract the network ID */ - iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); - if (iface == NULL || net_id == NULL) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - /* Ensure the network is actually a child of this interface */ - if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - id = strtoul(net_id, NULL, 10); - result = wpa_supplicant_remove_network(wpa_s, id); - if (result == -1) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - if (result == -2) { - reply = dbus_message_new_error( - message, WPAS_ERROR_REMOVE_NETWORK_ERROR, - "error removing the specified on this interface."); - goto out; - } - - reply = wpas_dbus_new_success_reply(message); - -out: - os_free(iface); - os_free(net_id); - return reply; -} - - -static const char * const dont_quote[] = { - "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", - "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", - "bssid", "scan_freq", "freq_list", NULL -}; - - -static dbus_bool_t should_quote_opt(const char *key) -{ - int i = 0; - - while (dont_quote[i] != NULL) { - if (os_strcmp(key, dont_quote[i]) == 0) - return FALSE; - i++; - } - return TRUE; -} - - -/** - * wpas_dbus_iface_set_network - Set options for a configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @ssid: wpa_ssid structure for a configured network - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "set" method call of a configured network. - */ -DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - DBusMessage *reply = NULL; - struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; - DBusMessageIter iter, iter_dict; - - dbus_message_iter_init(message, &iter); - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - char *value = NULL; - size_t size = 50; - int ret; - - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { - reply = wpas_dbus_new_invalid_opts_error(message, - NULL); - goto out; - } - - /* Type conversions, since wpa_supplicant wants strings */ - if (entry.type == DBUS_TYPE_ARRAY && - entry.array_type == DBUS_TYPE_BYTE) { - if (entry.array_len <= 0) - goto error; - - size = entry.array_len * 2 + 1; - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = wpa_snprintf_hex(value, size, - (u8 *) entry.bytearray_value, - entry.array_len); - if (ret <= 0) - goto error; - } else if (entry.type == DBUS_TYPE_STRING) { - if (should_quote_opt(entry.key)) { - size = os_strlen(entry.str_value); - /* Zero-length option check */ - if (size == 0) - goto error; - size += 3; /* For quotes and terminator */ - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = os_snprintf(value, size, "\"%s\"", - entry.str_value); - if (os_snprintf_error(size, ret)) - goto error; - } else { - value = os_strdup(entry.str_value); - if (value == NULL) - goto error; - } - } else if (entry.type == DBUS_TYPE_UINT32) { - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = os_snprintf(value, size, "%u", - entry.uint32_value); - if (os_snprintf_error(size, ret)) - goto error; - } else if (entry.type == DBUS_TYPE_INT32) { - value = os_zalloc(size); - if (value == NULL) - goto error; - ret = os_snprintf(value, size, "%d", - entry.int32_value); - if (os_snprintf_error(size, ret)) - goto error; - } else - goto error; - - if (wpa_config_set(ssid, entry.key, value, 0) < 0) - goto error; - - if ((os_strcmp(entry.key, "psk") == 0 && - value[0] == '"' && ssid->ssid_len) || - (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) - wpa_config_update_psk(ssid); - else if (os_strcmp(entry.key, "priority") == 0) - wpa_config_update_prio_list(wpa_s->conf); - - os_free(value); - wpa_dbus_dict_entry_clear(&entry); - continue; - - error: - os_free(value); - reply = wpas_dbus_new_invalid_opts_error(message, entry.key); - wpa_dbus_dict_entry_clear(&entry); - break; - } - - if (!reply) - reply = wpas_dbus_new_success_reply(message); - -out: - return reply; -} - - -/** - * wpas_dbus_iface_enable_network - Mark a configured network as enabled - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @ssid: wpa_ssid structure for a configured network - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "enable" method call of a configured network. - */ -DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - wpa_supplicant_enable_network(wpa_s, ssid); - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_disable_network - Mark a configured network as disabled - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * @ssid: wpa_ssid structure for a configured network - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "disable" method call of a configured network. - */ -DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) -{ - wpa_supplicant_disable_network(wpa_s, ssid); - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_select_network - Attempt association with a configured network - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "selectNetwork" method call of network interface. - */ -DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - const char *op; - struct wpa_ssid *ssid; - char *iface_obj_path = NULL; - char *network = NULL; - - if (os_strlen(dbus_message_get_signature(message)) == 0) { - /* Any network */ - ssid = NULL; - } else { - int nid; - - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, - NULL); - goto out; - } - - /* Extract the network number */ - iface_obj_path = wpas_dbus_decompose_object_path(op, - &network, - NULL); - if (iface_obj_path == NULL) { - reply = wpas_dbus_new_invalid_iface_error(message); - goto out; - } - /* Ensure the object path really points to this interface */ - if (network == NULL || !wpa_s->dbus_path || - os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - nid = strtoul(network, NULL, 10); - if (errno == EINVAL) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - - ssid = wpa_config_get_network(wpa_s->conf, nid); - if (ssid == NULL) { - reply = wpas_dbus_new_invalid_network_error(message); - goto out; - } - } - - /* Finally, associate with the network */ - wpa_supplicant_select_network(wpa_s, ssid); - - reply = wpas_dbus_new_success_reply(message); - -out: - os_free(iface_obj_path); - os_free(network); - return reply; -} - - -/** - * wpas_dbus_iface_disconnect - Terminate the current connection - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "disconnect" method call of network interface. - */ -DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - wpas_request_disconnection(wpa_s); - - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_set_ap_scan - Control roaming mode - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "setAPScan" method call. - */ -DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - dbus_uint32_t ap_scan = 1; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan, - DBUS_TYPE_INVALID)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) { - reply = wpas_dbus_new_invalid_opts_error(message, NULL); - goto out; - } - - reply = wpas_dbus_new_success_reply(message); - -out: - return reply; -} - - -/** - * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "setSmartcardModules" method call. - */ -DBusMessage * wpas_dbus_iface_set_smartcard_modules( - DBusMessage *message, struct wpa_supplicant *wpa_s) -{ - DBusMessageIter iter, iter_dict; - char *opensc_engine_path = NULL; - char *pkcs11_engine_path = NULL; - char *pkcs11_module_path = NULL; - struct wpa_dbus_dict_entry entry; - - if (!dbus_message_iter_init(message, &iter)) - goto error; - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) - goto error; - - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) - goto error; - if (!strcmp(entry.key, "opensc_engine_path") && - entry.type == DBUS_TYPE_STRING) { - os_free(opensc_engine_path); - opensc_engine_path = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (opensc_engine_path == NULL) - goto error; - } else if (!strcmp(entry.key, "pkcs11_engine_path") && - entry.type == DBUS_TYPE_STRING) { - os_free(pkcs11_engine_path); - pkcs11_engine_path = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (pkcs11_engine_path == NULL) - goto error; - } else if (!strcmp(entry.key, "pkcs11_module_path") && - entry.type == DBUS_TYPE_STRING) { - os_free(pkcs11_module_path); - pkcs11_module_path = os_strdup(entry.str_value); - wpa_dbus_dict_entry_clear(&entry); - if (pkcs11_module_path == NULL) - goto error; - } else { - wpa_dbus_dict_entry_clear(&entry); - goto error; - } - } - - os_free(wpa_s->conf->opensc_engine_path); - wpa_s->conf->opensc_engine_path = opensc_engine_path; - os_free(wpa_s->conf->pkcs11_engine_path); - wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path; - os_free(wpa_s->conf->pkcs11_module_path); - wpa_s->conf->pkcs11_module_path = pkcs11_module_path; - - wpa_sm_set_eapol(wpa_s->wpa, NULL); - eapol_sm_deinit(wpa_s->eapol); - wpa_s->eapol = NULL; - wpa_supplicant_init_eapol(wpa_s); - wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); - - return wpas_dbus_new_success_reply(message); - -error: - os_free(opensc_engine_path); - os_free(pkcs11_engine_path); - os_free(pkcs11_module_path); - return wpas_dbus_new_invalid_opts_error(message, NULL); -} - - -/** - * wpas_dbus_iface_get_state - Get interface state - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a STRING representing the current - * interface state - * - * Handler function for "state" method call. - */ -DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - const char *str_state; - - reply = dbus_message_new_method_return(message); - if (reply != NULL) { - str_state = wpa_supplicant_state_txt(wpa_s->wpa_state); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state, - DBUS_TYPE_INVALID); - } - - return reply; -} - - -/** - * wpas_dbus_iface_get_scanning - Get interface scanning state - * @message: Pointer to incoming dbus message - * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing whether the interface is scanning - * - * Handler function for "scanning" method call. - */ -DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; - - reply = dbus_message_new_method_return(message); - if (reply != NULL) { - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, - DBUS_TYPE_INVALID); - } else { - wpa_printf(MSG_ERROR, - "dbus: Not enough memory to return scanning state"); - } - - return reply; -} - - -#ifndef CONFIG_NO_CONFIG_BLOBS - -/** - * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Asks wpa_supplicant to internally store a one or more binary blobs. - */ -DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; - DBusMessageIter iter, iter_dict; - - dbus_message_iter_init(message, &iter); - - if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { - struct wpa_config_blob *blob; - - if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) { - reply = wpas_dbus_new_invalid_opts_error(message, - NULL); - break; - } - - if (entry.type != DBUS_TYPE_ARRAY || - entry.array_type != DBUS_TYPE_BYTE) { - reply = wpas_dbus_new_invalid_opts_error( - message, "Byte array expected."); - break; - } - - if ((entry.array_len <= 0) || (entry.array_len > 65536) || - !strlen(entry.key)) { - reply = wpas_dbus_new_invalid_opts_error( - message, "Invalid array size."); - break; - } - - blob = os_zalloc(sizeof(*blob)); - if (blob == NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "Not enough memory to add blob."); - break; - } - blob->data = os_zalloc(entry.array_len); - if (blob->data == NULL) { - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "Not enough memory to add blob data."); - os_free(blob); - break; - } - - blob->name = os_strdup(entry.key); - blob->len = entry.array_len; - os_memcpy(blob->data, (u8 *) entry.bytearray_value, - entry.array_len); - if (blob->name == NULL) { - wpa_config_free_blob(blob); - reply = dbus_message_new_error( - message, WPAS_ERROR_ADD_ERROR, - "Error adding blob."); - break; - } - - /* Success */ - if (!wpa_config_remove_blob(wpa_s->conf, blob->name)) - wpas_notify_blob_removed(wpa_s, blob->name); - wpa_config_set_blob(wpa_s->conf, blob); - wpas_notify_blob_added(wpa_s, blob->name); - - wpa_dbus_dict_entry_clear(&entry); - } - wpa_dbus_dict_entry_clear(&entry); - - return reply ? reply : wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_remove_blob - Remove named binary blobs - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Asks wpa_supplicant to remove one or more previously stored binary blobs. - */ -DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessageIter iter, array; - char *err_msg = NULL; - - dbus_message_iter_init(message, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - dbus_message_iter_recurse(&iter, &array); - while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { - const char *name; - - dbus_message_iter_get_basic(&array, &name); - if (!os_strlen(name)) - err_msg = "Invalid blob name."; - else if (wpa_config_remove_blob(wpa_s->conf, name) != 0) - err_msg = "Error removing blob."; - else - wpas_notify_blob_removed(wpa_s, name); - dbus_message_iter_next(&array); - } - - if (err_msg) - return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR, - err_msg); - - return wpas_dbus_new_success_reply(message); -} - -#endif /* CONFIG_NO_CONFIG_BLOBS */ - - -/** - * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: a dbus message containing a UINT32 indicating success (1) or - * failure (0), or returns a dbus error message with more information - * - * Handler function for "flush" method call. Handles requests for an - * interface with an optional "age" parameter that specifies the minimum - * age of a BSS to be flushed. - */ -DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - int flush_age = 0; - - if (os_strlen(dbus_message_get_signature(message)) != 0 && - !dbus_message_get_args(message, NULL, - DBUS_TYPE_INT32, &flush_age, - DBUS_TYPE_INVALID)) { - return wpas_dbus_new_invalid_opts_error(message, NULL); - } - - if (flush_age == 0) - wpa_bss_flush(wpa_s); - else - wpa_bss_flush_by_age(wpa_s, flush_age); - - return wpas_dbus_new_success_reply(message); -} diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h deleted file mode 100644 index e60ad06a032e..000000000000 --- a/wpa_supplicant/dbus/dbus_old_handlers.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface - * Copyright (c) 2006, Dan Williams and Red Hat, Inc. - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef CTRL_IFACE_DBUS_HANDLERS_H -#define CTRL_IFACE_DBUS_HANDLERS_H - -struct wpa_bss; - -DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message); -DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message); - -DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, - struct wpa_global *global); - -DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_bss *bss); - -DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid); - -DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid); - -DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, - struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid); - -DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_smartcard_modules( - DBusMessage *message, struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_iface_flush(DBusMessage *message, - struct wpa_supplicant *wpa_s); - -DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message); -DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, - const char *arg); - -#endif /* CTRL_IFACE_DBUS_HANDLERS_H */ - diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c deleted file mode 100644 index 5309a5301fc2..000000000000 --- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * WPA Supplicant / dbus-based control interface (WPS) - * Copyright (c) 2006, Dan Williams and Red Hat, 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 "../config.h" -#include "../wpa_supplicant_i.h" -#include "../wps_supplicant.h" -#include "dbus_old.h" -#include "dbus_old_handlers.h" - -/** - * wpas_dbus_iface_wps_pbc - Request credentials using WPS PBC method - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "wpsPbc" method call - */ -DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - char *arg_bssid = NULL; - u8 bssid[ETH_ALEN]; - int ret = 0; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, - DBUS_TYPE_INVALID)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - if (os_strcmp(arg_bssid, "any") == 0) - ret = wpas_wps_start_pbc(wpa_s, NULL, 0); - else if (!hwaddr_aton(arg_bssid, bssid)) - ret = wpas_wps_start_pbc(wpa_s, bssid, 0); - else { - return wpas_dbus_new_invalid_opts_error(message, - "Invalid BSSID"); - } - - if (ret < 0) { - return dbus_message_new_error( - message, WPAS_ERROR_WPS_PBC_ERROR, - "Could not start PBC negotiation"); - } - - return wpas_dbus_new_success_reply(message); -} - - -/** - * wpas_dbus_iface_wps_pin - Establish the PIN number of the enrollee - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "wpsPin" method call - */ -DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - DBusMessage *reply = NULL; - char *arg_bssid; - char *pin = NULL; - u8 bssid[ETH_ALEN], *_bssid = NULL; - int ret = 0; - char npin[9]; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, - DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - if (os_strcmp(arg_bssid, "any") == 0) - _bssid = NULL; - else if (!hwaddr_aton(arg_bssid, bssid)) - _bssid = bssid; - else { - return wpas_dbus_new_invalid_opts_error(message, - "Invalid BSSID"); - } - - if (os_strlen(pin) > 0) - ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0, - DEV_PW_DEFAULT); - else - ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, - DEV_PW_DEFAULT); - - if (ret < 0) { - return dbus_message_new_error(message, - WPAS_ERROR_WPS_PIN_ERROR, - "Could not init PIN"); - } - - reply = dbus_message_new_method_return(message); - if (reply == NULL) - return NULL; - - if (ret > 0) { - os_snprintf(npin, sizeof(npin), "%08d", ret); - pin = npin; - } - dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, - DBUS_TYPE_INVALID); - return reply; -} - - -/** - * wpas_dbus_iface_wps_reg - Request credentials using the PIN of the AP - * @message: Pointer to incoming dbus message - * @wpa_s: %wpa_supplicant data structure - * Returns: A dbus message containing a UINT32 indicating success (1) or - * failure (0) - * - * Handler function for "wpsReg" method call - */ -DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, - struct wpa_supplicant *wpa_s) -{ - char *arg_bssid; - char *pin = NULL; - u8 bssid[ETH_ALEN]; - int ret = 0; - - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, - DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) - return wpas_dbus_new_invalid_opts_error(message, NULL); - - if (!hwaddr_aton(arg_bssid, bssid)) - ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL); - else { - return wpas_dbus_new_invalid_opts_error(message, - "Invalid BSSID"); - } - - if (ret < 0) { - return dbus_message_new_error(message, - WPAS_ERROR_WPS_REG_ERROR, - "Could not request credentials"); - } - - return wpas_dbus_new_success_reply(message); -} diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in deleted file mode 100644 index a75918f9380b..000000000000 --- a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in +++ /dev/null @@ -1,5 +0,0 @@ -[D-BUS Service] -Name=fi.epitest.hostap.WPASupplicant -Exec=@BINDIR@/wpa_supplicant -u -User=root -SystemdService=wpa_supplicant.service diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index af281e56d2e1..88cd79085f6d 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -109,10 +109,7 @@ CONFIG_EAP_PEAP=y CONFIG_EAP_TTLS=y # EAP-FAST -# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed -# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., -# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. -#CONFIG_EAP_FAST=y +CONFIG_EAP_FAST=y # EAP-GTC CONFIG_EAP_GTC=y @@ -127,10 +124,10 @@ CONFIG_EAP_OTP=y #CONFIG_EAP_PSK=y # EAP-pwd (secure authentication using only a password) -#CONFIG_EAP_PWD=y +CONFIG_EAP_PWD=y # EAP-PAX -#CONFIG_EAP_PAX=y +CONFIG_EAP_PAX=y # LEAP CONFIG_EAP_LEAP=y @@ -146,18 +143,18 @@ CONFIG_EAP_LEAP=y #CONFIG_USIM_SIMULATOR=y # EAP-SAKE -#CONFIG_EAP_SAKE=y +CONFIG_EAP_SAKE=y # EAP-GPSK -#CONFIG_EAP_GPSK=y +CONFIG_EAP_GPSK=y # Include support for optional SHA256 cipher suite in EAP-GPSK -#CONFIG_EAP_GPSK_SHA256=y +CONFIG_EAP_GPSK_SHA256=y # EAP-TNC and related Trusted Network Connect support (experimental) -#CONFIG_EAP_TNC=y +CONFIG_EAP_TNC=y # Wi-Fi Protected Setup (WPS) -#CONFIG_WPS=y +CONFIG_WPS=y # Enable WPS external registrar functionality #CONFIG_WPS_ER=y # Disable credentials for an open network by default when acting as a WPS @@ -167,7 +164,7 @@ CONFIG_EAP_LEAP=y #CONFIG_WPS_NFC=y # EAP-IKEv2 -#CONFIG_EAP_IKEV2=y +CONFIG_EAP_IKEV2=y # EAP-EKE #CONFIG_EAP_EKE=y @@ -235,6 +232,9 @@ CONFIG_CTRL_IFACE=y # wpa_passphrase). This saves about 0.5 kB in code size. #CONFIG_NO_WPA_PASSPHRASE=y +# Simultaneous Authentication of Equals (SAE), WPA3-Personal +CONFIG_SAE=y + # Disable scan result processing (ap_mode=1) to save code size by about 1 kB. # This can be used if ap_scan=1 mode is never enabled. #CONFIG_NO_SCAN_PROCESSING=y @@ -299,7 +299,10 @@ CONFIG_BACKEND=file # IEEE 802.11w (management frame protection), also known as PMF # Driver support is also needed for IEEE 802.11w. -#CONFIG_IEEE80211W=y +CONFIG_IEEE80211W=y + +# Support Operating Channel Validation +#CONFIG_OCV=y # Select TLS implementation # openssl = OpenSSL (default) @@ -349,16 +352,12 @@ CONFIG_BACKEND=file #CONFIG_NDIS_EVENTS_INTEGRATED=y #PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" -# Add support for old DBus control interface -# (fi.epitest.hostap.WPASupplicant) -#CONFIG_CTRL_IFACE_DBUS=y - # Add support for new DBus control interface # (fi.w1.hostap.wpa_supplicant1) -#CONFIG_CTRL_IFACE_DBUS_NEW=y +CONFIG_CTRL_IFACE_DBUS_NEW=y # Add introspection support for new DBus control interface -#CONFIG_CTRL_IFACE_DBUS_INTRO=y +CONFIG_CTRL_IFACE_DBUS_INTRO=y # Add support for loading EAP methods dynamically as shared libraries. # When this option is enabled, each EAP method can be either included @@ -382,13 +381,13 @@ CONFIG_BACKEND=file #CONFIG_DYNAMIC_EAP_METHODS=y # IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode -#CONFIG_IEEE80211R=y +CONFIG_IEEE80211R=y # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) -#CONFIG_DEBUG_FILE=y +CONFIG_DEBUG_FILE=y # Send debug messages to syslog instead of stdout -#CONFIG_DEBUG_SYSLOG=y +CONFIG_DEBUG_SYSLOG=y # Set syslog facility for debug messages #CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON @@ -458,12 +457,17 @@ CONFIG_BACKEND=file # that meet the requirements described above. #CONFIG_NO_RANDOM_POOL=y +# Should we attempt to use the getrandom(2) call that provides more reliable +# yet secure randomness source than /dev/random on Linux 3.17 and newer. +# Requires glibc 2.25 to build, falls back to /dev/random if unavailable. +#CONFIG_GETRANDOM=y + # IEEE 802.11n (High Throughput) support (mainly for AP mode) -#CONFIG_IEEE80211N=y +CONFIG_IEEE80211N=y # IEEE 802.11ac (Very High Throughput) support (mainly for AP mode) # (depends on CONFIG_IEEE80211N) -#CONFIG_IEEE80211AC=y +CONFIG_IEEE80211AC=y # Wireless Network Management (IEEE Std 802.11v-2011) # Note: This is experimental and not complete implementation. @@ -473,10 +477,10 @@ CONFIG_BACKEND=file # This can be used to enable functionality to improve interworking with # external networks (GAS/ANQP to learn more about the networks and network # selection based on available credentials). -#CONFIG_INTERWORKING=y +CONFIG_INTERWORKING=y # Hotspot 2.0 -#CONFIG_HS20=y +CONFIG_HS20=y # Enable interface matching in wpa_supplicant #CONFIG_MATCH_IFACE=y @@ -489,20 +493,20 @@ CONFIG_BACKEND=file # should be noted that this is mainly aimed at simple cases like # WPA2-Personal while more complex configurations like WPA2-Enterprise with an # external RADIUS server can be supported with hostapd. -#CONFIG_AP=y +CONFIG_AP=y # P2P (Wi-Fi Direct) # This can be used to enable P2P support in wpa_supplicant. See README-P2P for # more information on P2P operations. -#CONFIG_P2P=y +CONFIG_P2P=y # Enable TDLS support #CONFIG_TDLS=y -# Wi-Fi Direct -# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# Wi-Fi Display +# This can be used to enable Wi-Fi Display extensions for P2P using an external # program to control the additional information exchanges in the messages. -#CONFIG_WIFI_DISPLAY=y +CONFIG_WIFI_DISPLAY=y # Autoscan # This can be used to enable automatic scan support in wpa_supplicant. @@ -561,8 +565,6 @@ CONFIG_BACKEND=file #CONFIG_MBO=y # Fast Initial Link Setup (FILS) (IEEE 802.11ai) -# Note: This is an experimental and not yet complete implementation. This -# should not be enabled for production use. #CONFIG_FILS=y # FILS shared key authentication with PFS #CONFIG_FILS_SK_PFS=y @@ -570,7 +572,7 @@ CONFIG_BACKEND=file # Support RSN on IBSS networks # This is needed to be able to use mode=1 network profile with proto=RSN and # key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None). -#CONFIG_IBSS_RSN=y +CONFIG_IBSS_RSN=y # External PMKSA cache control # This can be used to enable control interface commands that allow the current @@ -585,7 +587,7 @@ CONFIG_BACKEND=file # operations for roaming within an ESS (same SSID). See the bgscan parameter in # the wpa_supplicant.conf file for more details. # Periodic background scans based on signal strength -#CONFIG_BGSCAN_SIMPLE=y +CONFIG_BGSCAN_SIMPLE=y # Learn channels used by the network and try to avoid bgscans on other # channels (experimental) #CONFIG_BGSCAN_LEARN=y @@ -593,3 +595,8 @@ CONFIG_BACKEND=file # Opportunistic Wireless Encryption (OWE) # Experimental implementation of draft-harkins-owe-07.txt #CONFIG_OWE=y + +# Device Provisioning Protocol (DPP) +# This requires CONFIG_IEEE80211W=y to be enabled, too. (see +# wpa_supplicant/README-DPP for details) +CONFIG_DPP=y diff --git a/wpa_supplicant/doc/docbook/eapol_test.8 b/wpa_supplicant/doc/docbook/eapol_test.8 index 1ae1ac1c93ea..717f79f8d8dc 100644 --- a/wpa_supplicant/doc/docbook/eapol_test.8 +++ b/wpa_supplicant/doc/docbook/eapol_test.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "EAPOL_TEST" "8" "02 December 2018" "" "" +.TH "EAPOL_TEST" "8" "21 April 2019" "" "" .SH NAME eapol_test \- EAP peer and RADIUS client testing @@ -115,7 +115,7 @@ Save configuration after authentication. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml index ae6bafecfa15..e7705abf16fb 100644 --- a/wpa_supplicant/doc/docbook/eapol_test.sgml +++ b/wpa_supplicant/doc/docbook/eapol_test.sgml @@ -194,7 +194,7 @@ eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8 index e2b894b6ae24..4312f73c02c2 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.8 +++ b/wpa_supplicant/doc/docbook/wpa_background.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_BACKGROUND" "8" "02 December 2018" "" "" +.TH "WPA_BACKGROUND" "8" "21 April 2019" "" "" .SH NAME wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i @@ -75,7 +75,7 @@ pre-authentication, and PMKSA caching). \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml index d3e4dbe2a5a8..f6a0ca8b5b55 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.sgml +++ b/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -90,7 +90,7 @@ Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8 index ec96a4dfa93e..600a8f6ca7ed 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.8 +++ b/wpa_supplicant/doc/docbook/wpa_cli.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_CLI" "8" "02 December 2018" "" "" +.TH "WPA_CLI" "8" "21 April 2019" "" "" .SH NAME wpa_cli \- WPA command line client @@ -210,7 +210,7 @@ exit wpa_cli \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml index 766dd2cc1370..dc7fee46a27f 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.sgml +++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -345,7 +345,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8 index 2d7e74e48e61..c4c133b70370 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.8 +++ b/wpa_supplicant/doc/docbook/wpa_gui.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_GUI" "8" "02 December 2018" "" "" +.TH "WPA_GUI" "8" "21 April 2019" "" "" .SH NAME wpa_gui \- WPA Graphical User Interface @@ -51,7 +51,7 @@ icon pop-up messages. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml index 91662d54a1fc..31214e3ed020 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.sgml +++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -91,7 +91,7 @@ Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8 index 20e7155cf725..a56bcf57b8f4 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.8 +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_PASSPHRASE" "8" "02 December 2018" "" "" +.TH "WPA_PASSPHRASE" "8" "21 April 2019" "" "" .SH NAME wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID @@ -31,7 +31,7 @@ passphrase will be read from standard input. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml index 2f86b0bdf987..ed9baf1eac85 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -62,7 +62,7 @@ Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8 index 4064d1ba9164..6388293e28b4 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.8 +++ b/wpa_supplicant/doc/docbook/wpa_priv.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_PRIV" "8" "02 December 2018" "" "" +.TH "WPA_PRIV" "8" "21 April 2019" "" "" .SH NAME wpa_priv \- wpa_supplicant privilege separation helper @@ -111,7 +111,7 @@ processes at the same time, if desired. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml index 4a5f319db3b2..dd445651ac0a 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.sgml +++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -137,7 +137,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8 index 16a94058be90..e91e6ed61d5a 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.8 +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.8 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_SUPPLICANT" "8" "02 December 2018" "" "" +.TH "WPA_SUPPLICANT" "8" "21 April 2019" "" "" .SH NAME wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant @@ -217,8 +217,13 @@ list of supported driver backends that may be used with the -D option on your system, refer to the help output of wpa_supplicant (\fBwpa_supplicant -h\fR). .TP +\fBnl80211\fR +Uses the modern Linux nl80211/cfg80211 netlink-based +interface (most new drivers). +.TP \fBwext\fR -Linux wireless extensions (generic). +Uses the legacy Linux wireless extensions ioctl-based +interface (older hardware/drivers). .TP \fBwired\fR wpa_supplicant wired Ethernet driver @@ -325,7 +330,7 @@ Include timestamp in debug messages. Enable DBus control interface. If enabled, interface definitions may be omitted. (This is only available if \fBwpa_supplicant\fR was built with -the CONFIG_DBUS option.) +the CONFIG_CTRL_IFACE_DBUS_NEW option.) .TP \fB-v\fR Show version. @@ -392,7 +397,11 @@ wpa_supplicant \\ Current hardware/software requirements: .TP 0.2i \(bu -Linux kernel 2.4.x or 2.6.x with Linux Wireless +Linux kernel 2.6.30 or higher with +nl80211/cfg80211 support +.TP 0.2i +\(bu +Linux kernel 2.4.x or higher with Linux Wireless Extensions v15 or newer .TP 0.2i \(bu @@ -403,6 +412,9 @@ Microsoft Windows with WinPcap (at least WinXP, may work with other versions) .SH "SUPPORTED DRIVERS" .TP +\fBLinux nl80211/cfg80211\fR +This is the preferred driver for Linux. +.TP \fBLinux wireless extensions\fR In theory, any driver that supports Linux wireless extensions can be used with IEEE 802.1X (i.e., not WPA) when @@ -532,7 +544,7 @@ in. \fBwpa_passphrase\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2018, +wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 index 07461130085e..43649238e310 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 @@ -3,7 +3,7 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "WPA_SUPPLICANT.CONF" "5" "02 December 2018" "" "" +.TH "WPA_SUPPLICANT.CONF" "5" "21 April 2019" "" "" .SH NAME wpa_supplicant.conf \- configuration file for wpa_supplicant diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index eeb9c07305b9..aaff15002784 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -245,10 +245,19 @@ (wpa_supplicant -h). + + nl80211 + + Uses the modern Linux nl80211/cfg80211 netlink-based + interface (most new drivers). + + + wext - Linux wireless extensions (generic). + Uses the legacy Linux wireless extensions ioctl-based + interface (older hardware/drivers). @@ -462,7 +471,7 @@ Enable DBus control interface. If enabled, interface definitions may be omitted. (This is only available if wpa_supplicant was built with - the CONFIG_DBUS option.) + the CONFIG_CTRL_IFACE_DBUS_NEW option.) @@ -538,10 +547,14 @@ wpa_supplicant \ - Linux kernel 2.4.x or 2.6.x with Linux Wireless - Extensions v15 or newer + Linux kernel 2.6.30 or higher with + nl80211/cfg80211 support + + Linux kernel 2.4.x or higher with Linux Wireless + Extensions v15 or newer + FreeBSD 6-CURRENT @@ -557,6 +570,13 @@ wpa_supplicant \ Supported Drivers + + Linux nl80211/cfg80211 + + This is the preferred driver for Linux. + + + Linux wireless extensions @@ -729,7 +749,7 @@ fi Legal - wpa_supplicant is copyright (c) 2003-2018, + wpa_supplicant is copyright (c) 2003-2019, Jouni Malinen j@w1.fi and contributors. All Rights Reserved. diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 7bc46610a971..e003a8514edb 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -1,7 +1,7 @@ /* * wpa_supplicant - DPP * Copyright (c) 2017, Qualcomm Atheros, Inc. - * Copyright (c) 2018, The Linux Foundation + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -52,34 +52,6 @@ static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const u8 TRANSACTION_ID = 1; -static struct dpp_configurator * -dpp_configurator_get_id(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_configurator *conf; - - dl_list_for_each(conf, &wpa_s->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id == id) - return conf; - } - return NULL; -} - - -static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s) -{ - struct dpp_bootstrap_info *bi; - unsigned int max_id = 0; - - dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, - list) { - if (bi->id > max_id) - max_id = bi->id; - } - return max_id + 1; -} - - /** * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code * @wpa_s: Pointer to wpa_supplicant data @@ -91,13 +63,10 @@ int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) struct dpp_bootstrap_info *bi; struct dpp_authentication *auth = wpa_s->dpp_auth; - bi = dpp_parse_qr_code(cmd); + bi = dpp_add_qr_code(wpa_s->dpp, cmd); if (!bi) return -1; - bi->id = wpas_dpp_next_id(wpa_s); - dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); - if (auth && auth->response_pending && dpp_notify_new_qr_code(auth, bi) == 1) { wpa_printf(MSG_DEBUG, @@ -118,195 +87,6 @@ int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) } -static char * get_param(const char *cmd, const char *param) -{ - const char *pos, *end; - char *val; - size_t len; - - pos = os_strstr(cmd, param); - if (!pos) - return NULL; - - pos += os_strlen(param); - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - val = os_malloc(len + 1); - if (!val) - return NULL; - os_memcpy(val, pos, len); - val[len] = '\0'; - return val; -} - - -int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd) -{ - char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - size_t len; - int ret = -1; - struct dpp_bootstrap_info *bi; - - bi = os_zalloc(sizeof(*bi)); - if (!bi) - goto fail; - - if (os_strstr(cmd, "type=qrcode")) - bi->type = DPP_BOOTSTRAP_QR_CODE; - else if (os_strstr(cmd, "type=pkex")) - bi->type = DPP_BOOTSTRAP_PKEX; - else - goto fail; - - chan = get_param(cmd, " chan="); - mac = get_param(cmd, " mac="); - info = get_param(cmd, " info="); - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - pk = dpp_keygen(bi, curve, privkey, privkey_len); - if (!pk) - goto fail; - - len = 4; /* "DPP:" */ - if (chan) { - if (dpp_parse_uri_chan_list(bi, chan) < 0) - goto fail; - len += 3 + os_strlen(chan); /* C:...; */ - } - if (mac) { - if (dpp_parse_uri_mac(bi, mac) < 0) - goto fail; - len += 3 + os_strlen(mac); /* M:...; */ - } - if (info) { - if (dpp_parse_uri_info(bi, info) < 0) - goto fail; - len += 3 + os_strlen(info); /* I:...; */ - } - len += 4 + os_strlen(pk); - bi->uri = os_malloc(len + 1); - if (!bi->uri) - goto fail; - os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", - chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", - mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", - info ? "I:" : "", info ? info : "", info ? ";" : "", - pk); - bi->id = wpas_dpp_next_id(wpa_s); - dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); - ret = bi->id; - bi = NULL; -fail: - os_free(curve); - os_free(pk); - os_free(chan); - os_free(mac); - os_free(info); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_bootstrap_info_free(bi); - return ret; -} - - -static struct dpp_bootstrap_info * -dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, - list) { - if (bi->id == id) - return bi; - } - return NULL; -} - - -static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_bootstrap_info *bi, *tmp; - int found = 0; - - dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap, - struct dpp_bootstrap_info, list) { - if (id && bi->id != id) - continue; - found = 1; - dl_list_del(&bi->list); - dpp_bootstrap_info_free(bi); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_bootstrap_del(wpa_s, id_val); -} - - -const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, - unsigned int id) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(wpa_s, id); - if (!bi) - return NULL; - return bi->uri; -} - - -int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id, - char *reply, int reply_size) -{ - struct dpp_bootstrap_info *bi; - - bi = dpp_bootstrap_get_id(wpa_s, id); - if (!bi) - return -1; - return os_snprintf(reply, reply_size, "type=%s\n" - "mac_addr=" MACSTR "\n" - "info=%s\n" - "num_freq=%u\n" - "curve=%s\n", - dpp_bootstrap_type_txt(bi->type), - MAC2STR(bi->mac_addr), - bi->info ? bi->info : "", - bi->num_freq, - bi->curve->name); -} - - static void wpas_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -364,6 +144,18 @@ static void wpas_dpp_auth_resp_retry(struct wpa_supplicant *wpa_s) } +static void wpas_dpp_try_to_connect(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network"); + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_s->scan_runs = 0; + wpa_s->normal_scans = 0; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, @@ -387,6 +179,17 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_DPP2 + if (auth->connect_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Try to connect after completed configuration result"); + wpas_dpp_try_to_connect(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } +#endif /* CONFIG_DPP2 */ + if (wpa_s->dpp_auth->remove_on_tx_status) { wpa_printf(MSG_DEBUG, "DPP: Terminate authentication exchange due to an earlier error"); @@ -527,176 +330,6 @@ static void wpas_dpp_set_testing_options(struct wpa_supplicant *wpa_s, } -static int wpas_dpp_set_configurator(struct wpa_supplicant *wpa_s, - struct dpp_authentication *auth, - const char *cmd) -{ - const char *pos, *end; - struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; - struct dpp_configurator *conf = NULL; - u8 ssid[32] = { "test" }; - size_t ssid_len = 4; - char pass[64] = { }; - size_t pass_len = 0; - u8 psk[PMK_LEN]; - int psk_set = 0; - char *group_id = NULL; - - if (!cmd) - return 0; - - wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); - pos = os_strstr(cmd, " ssid="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); - ssid_len /= 2; - if (ssid_len > sizeof(ssid) || - hexstr2bin(pos, ssid, ssid_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " pass="); - if (pos) { - pos += 6; - end = os_strchr(pos, ' '); - pass_len = end ? (size_t) (end - pos) : os_strlen(pos); - pass_len /= 2; - if (pass_len > sizeof(pass) - 1 || pass_len < 8 || - hexstr2bin(pos, (u8 *) pass, pass_len) < 0) - goto fail; - } - - pos = os_strstr(cmd, " psk="); - if (pos) { - pos += 5; - if (hexstr2bin(pos, psk, PMK_LEN) < 0) - goto fail; - psk_set = 1; - } - - pos = os_strstr(cmd, " group_id="); - if (pos) { - size_t group_id_len; - - pos += 10; - end = os_strchr(pos, ' '); - group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); - group_id = os_malloc(group_id_len + 1); - if (!group_id) - goto fail; - os_memcpy(group_id, pos, group_id_len); - group_id[group_id_len] = '\0'; - } - - if (os_strstr(cmd, " conf=sta-")) { - conf_sta = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_sta) - goto fail; - os_memcpy(conf_sta->ssid, ssid, ssid_len); - conf_sta->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=sta-psk") || - os_strstr(cmd, " conf=sta-sae") || - os_strstr(cmd, " conf=sta-psk-sae")) { - if (os_strstr(cmd, " conf=sta-psk-sae")) - conf_sta->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=sta-sae")) - conf_sta->akm = DPP_AKM_SAE; - else - conf_sta->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_sta->psk, psk, PMK_LEN); - } else if (pass_len > 0) { - conf_sta->passphrase = os_strdup(pass); - if (!conf_sta->passphrase) - goto fail; - } else { - goto fail; - } - } else if (os_strstr(cmd, " conf=sta-dpp")) { - conf_sta->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_sta->group_id = group_id; - group_id = NULL; - } - } - - if (os_strstr(cmd, " conf=ap-")) { - conf_ap = os_zalloc(sizeof(struct dpp_configuration)); - if (!conf_ap) - goto fail; - os_memcpy(conf_ap->ssid, ssid, ssid_len); - conf_ap->ssid_len = ssid_len; - if (os_strstr(cmd, " conf=ap-psk") || - os_strstr(cmd, " conf=ap-sae") || - os_strstr(cmd, " conf=ap-psk-sae")) { - if (os_strstr(cmd, " conf=ap-psk-sae")) - conf_ap->akm = DPP_AKM_PSK_SAE; - else if (os_strstr(cmd, " conf=ap-sae")) - conf_ap->akm = DPP_AKM_SAE; - else - conf_ap->akm = DPP_AKM_PSK; - if (psk_set) { - os_memcpy(conf_ap->psk, psk, PMK_LEN); - } else { - conf_ap->passphrase = os_strdup(pass); - if (!conf_ap->passphrase) - goto fail; - } - } else if (os_strstr(cmd, " conf=ap-dpp")) { - conf_ap->akm = DPP_AKM_DPP; - } else { - goto fail; - } - if (os_strstr(cmd, " group_id=")) { - conf_ap->group_id = group_id; - group_id = NULL; - } - } - - pos = os_strstr(cmd, " expiry="); - if (pos) { - long int val; - - pos += 8; - val = strtol(pos, NULL, 0); - if (val <= 0) - goto fail; - if (conf_sta) - conf_sta->netaccesskey_expiry = val; - if (conf_ap) - conf_ap->netaccesskey_expiry = val; - } - - pos = os_strstr(cmd, " configurator="); - if (pos) { - pos += 14; - conf = dpp_configurator_get_id(wpa_s, atoi(pos)); - if (!conf) { - wpa_printf(MSG_INFO, - "DPP: Could not find the specified configurator"); - goto fail; - } - } - auth->conf_sta = conf_sta; - auth->conf_ap = conf_ap; - auth->conf = conf; - os_free(group_id); - return 0; - -fail: - wpa_msg(wpa_s, MSG_INFO, "DPP: Failed to set configurator parameters"); - dpp_configuration_free(conf_sta); - dpp_configuration_free(conf_ap); - os_free(group_id); - return -1; -} - - static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -809,7 +442,7 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) if (!pos) return -1; pos += 6; - peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + peer_bi = dpp_bootstrap_get_id(wpa_s->dpp, atoi(pos)); if (!peer_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified peer"); @@ -819,7 +452,7 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) pos = os_strstr(cmd, " own="); if (pos) { pos += 5; - own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + own_bi = dpp_bootstrap_get_id(wpa_s->dpp, atoi(pos)); if (!own_bi) { wpa_printf(MSG_INFO, "DPP: Could not find bootstrapping info for the identified local entry"); @@ -872,7 +505,7 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) if (!wpa_s->dpp_auth) goto fail; wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); - if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd) < 0) { + if (dpp_set_configurator(wpa_s->dpp, wpa_s, wpa_s->dpp_auth, cmd) < 0) { dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; goto fail; @@ -942,6 +575,7 @@ static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit) wpa_printf(MSG_DEBUG, "DPP: Failed to request the driver to remain on channel (%u MHz) for listen", lwork->freq); + wpa_s->dpp_listen_freq = 0; wpas_dpp_listen_work_done(wpa_s); wpa_s->dpp_pending_listen_freq = 0; return; @@ -1055,7 +689,10 @@ static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, { const u8 *r_bootstrap, *i_bootstrap; u16 r_bootstrap_len, i_bootstrap_len; - struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; + + if (!wpa_s->dpp) + return; wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, MAC2STR(src)); @@ -1082,28 +719,8 @@ static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, /* Try to find own and peer bootstrapping key matches based on the * received hash values */ - dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, - list) { - if (!own_bi && bi->own && - os_memcmp(bi->pubkey_hash, r_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching own bootstrapping information"); - own_bi = bi; - } - - if (!peer_bi && !bi->own && - os_memcmp(bi->pubkey_hash, i_bootstrap, - SHA256_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, - "DPP: Found matching peer bootstrapping information"); - peer_bi = bi; - } - - if (own_bi && peer_bi) - break; - } - + dpp_bootstrap_find_pair(wpa_s->dpp, i_bootstrap, r_bootstrap, + &own_bi, &peer_bi); if (!own_bi) { wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL "No matching own bootstrapping key found - ignore message"); @@ -1126,8 +743,8 @@ static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, return; } wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); - if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, - wpa_s->dpp_configurator_params) < 0) { + if (dpp_set_configurator(wpa_s->dpp, wpa_s, wpa_s->dpp_auth, + wpa_s->dpp_configurator_params) < 0) { dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; return; @@ -1164,6 +781,27 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; +#ifdef CONFIG_DPP2 + if (auth->akm == DPP_AKM_SAE) { +#ifdef CONFIG_SAE + struct wpa_driver_capa capa; + int res; + + res = wpa_drv_get_capa(wpa_s, &capa); + if (res == 0 && + !(capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { + wpa_printf(MSG_DEBUG, + "DPP: SAE not supported by the driver"); + return NULL; + } +#else /* CONFIG_SAE */ + wpa_printf(MSG_DEBUG, "DPP: SAE not supported in the build"); + return NULL; +#endif /* CONFIG_SAE */ + } +#endif /* CONFIG_DPP2 */ + ssid = wpa_config_add_network(wpa_s->conf); if (!ssid) return NULL; @@ -1206,12 +844,14 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry; } - if (!auth->connector) { - ssid->key_mgmt = 0; - if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_PSK_SAE) + if (!auth->connector || dpp_akm_psk(auth->akm) || + dpp_akm_sae(auth->akm)) { + if (!auth->connector) + ssid->key_mgmt = 0; + if (dpp_akm_psk(auth->akm)) ssid->key_mgmt |= WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_FT_PSK; - if (auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) + if (dpp_akm_sae(auth->akm)) ssid->key_mgmt |= WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE; ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; @@ -1235,35 +875,47 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, } -static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s, - struct dpp_authentication *auth) +static int wpas_dpp_process_config(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) { struct wpa_ssid *ssid; if (wpa_s->conf->dpp_config_processing < 1) - return; + return 0; ssid = wpas_dpp_add_network(wpa_s, auth); if (!ssid) - return; + return -1; wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id); - if (wpa_s->conf->dpp_config_processing < 2) - return; + if (wpa_s->conf->dpp_config_processing == 2) + ssid->disabled = 0; - wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network"); - ssid->disabled = 0; - wpa_s->disconnected = 0; - wpa_s->reassociate = 1; - wpa_s->scan_runs = 0; - wpa_s->normal_scans = 0; - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_printf(MSG_DEBUG, "DPP: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ + + if (wpa_s->conf->dpp_config_processing < 2) + return 0; + +#ifdef CONFIG_DPP2 + if (auth->peer_version >= 2) { + wpa_printf(MSG_DEBUG, + "DPP: Postpone connection attempt to wait for completion of DPP Configuration Result"); + auth->connect_on_tx_status = 1; + return 0; + } +#endif /* CONFIG_DPP2 */ + + wpas_dpp_try_to_connect(wpa_s); + return 0; } -static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, - struct dpp_authentication *auth) +static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) { wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED); if (auth->ssid_len) @@ -1315,7 +967,7 @@ static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, } } - wpas_dpp_process_config(wpa_s, auth); + return wpas_dpp_process_config(wpa_s, auth); } @@ -1327,6 +979,8 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, struct wpa_supplicant *wpa_s = ctx; const u8 *pos; struct dpp_authentication *auth = wpa_s->dpp_auth; + int res; + enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED; wpa_s->dpp_gas_dialog_token = -1; @@ -1364,13 +1018,46 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, goto fail; } - wpas_dpp_handle_config_obj(wpa_s, auth); - dpp_auth_deinit(wpa_s->dpp_auth); - wpa_s->dpp_auth = NULL; - return; + res = wpas_dpp_handle_config_obj(wpa_s, auth); + if (res < 0) + goto fail; + status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_REJECT_CONFIG) { + wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object"); + status = DPP_STATUS_CONFIG_REJECTED; + } +#endif /* CONFIG_TESTING_OPTIONS */ fail: - wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + if (status != DPP_STATUS_OK) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); +#ifdef CONFIG_DPP2 + if (auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); + msg = dpp_build_conf_result(auth, status); + if (!msg) + goto fail2; + + wpa_msg(wpa_s, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(addr), auth->curr_freq, + DPP_PA_CONFIGURATION_RESULT); + offchannel_send_action(wpa_s, auth->curr_freq, + addr, wpa_s->own_addr, broadcast, + wpabuf_head(msg), + wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + + /* This exchange will be terminated in the TX status handler */ + return; + } +fail2: +#endif /* CONFIG_DPP2 */ dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; } @@ -1379,7 +1066,7 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s) { struct dpp_authentication *auth = wpa_s->dpp_auth; - struct wpabuf *buf, *conf_req; + struct wpabuf *buf; char json[100]; int res; @@ -1400,34 +1087,13 @@ static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s) offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); - conf_req = dpp_build_conf_req(auth, json); - if (!conf_req) { + buf = dpp_build_conf_req(auth, json); + if (!buf) { wpa_printf(MSG_DEBUG, "DPP: No configuration request data available"); return; } - buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); - if (!buf) { - wpabuf_free(conf_req); - return; - } - - /* Advertisement Protocol IE */ - wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); - wpabuf_put_u8(buf, 8); /* Length */ - wpabuf_put_u8(buf, 0x7f); - wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, DPP_OUI_TYPE); - wpabuf_put_u8(buf, 0x01); - - /* GAS Query */ - wpabuf_put_le16(buf, wpabuf_len(conf_req)); - wpabuf_put_buf(buf, conf_req); - wpabuf_free(conf_req); - wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", MAC2STR(auth->peer_mac_addr), auth->curr_freq); @@ -1553,6 +1219,62 @@ static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src, } +#ifdef CONFIG_DPP2 + +static void wpas_dpp_config_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + if (!auth || !auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Configuration Result"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; +} + + +static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + enum dpp_status_error status; + + wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR, + MAC2STR(src)); + + if (!auth || !auth->waiting_conf_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for result - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + status = dpp_conf_result_rx(auth, hdr, buf, len); + + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + if (status == DPP_STATUS_OK) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL); +} + +#endif /* CONFIG_DPP2 */ + + static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len) @@ -1913,27 +1635,12 @@ static struct dpp_bootstrap_info * wpas_dpp_pkex_finish(struct wpa_supplicant *wpa_s, const u8 *peer, unsigned int freq) { - struct dpp_pkex *pkex = wpa_s->dpp_pkex; struct dpp_bootstrap_info *bi; - bi = os_zalloc(sizeof(*bi)); + bi = dpp_pkex_finish(wpa_s->dpp, wpa_s->dpp_pkex, peer, freq); if (!bi) return NULL; - bi->id = wpas_dpp_next_id(wpa_s); - bi->type = DPP_BOOTSTRAP_PKEX; - os_memcpy(bi->mac_addr, peer, ETH_ALEN); - bi->num_freq = 1; - bi->freq[0] = freq; - bi->curve = pkex->own_bi->curve; - bi->pubkey = pkex->peer_bootstrap_key; - pkex->peer_bootstrap_key = NULL; - dpp_pkex_free(pkex); wpa_s->dpp_pkex = NULL; - if (dpp_bootstrap_key_hash(bi) < 0) { - dpp_bootstrap_info_free(bi); - return NULL; - } - dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); return bi; } @@ -2096,6 +1803,11 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len, freq); break; +#ifdef CONFIG_DPP2 + case DPP_PA_CONFIGURATION_RESULT: + wpas_dpp_rx_conf_result(wpa_s, src, hdr, buf, len); + break; +#endif /* CONFIG_DPP2 */ default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -2165,6 +1877,21 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok) ok); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); +#ifdef CONFIG_DPP2 + if (ok && auth->peer_version >= 2 && + auth->conf_resp_status == DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); + auth->waiting_conf_result = 1; + auth->conf_resp = NULL; + wpabuf_free(resp); + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, + wpa_s, NULL); + eloop_register_timeout(2, 0, + wpas_dpp_config_result_wait_timeout, + wpa_s, NULL); + return; + } +#endif /* CONFIG_DPP2 */ offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); if (ok) @@ -2177,93 +1904,6 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok) } -static unsigned int wpas_dpp_next_configurator_id(struct wpa_supplicant *wpa_s) -{ - struct dpp_configurator *conf; - unsigned int max_id = 0; - - dl_list_for_each(conf, &wpa_s->dpp_configurator, - struct dpp_configurator, list) { - if (conf->id > max_id) - max_id = conf->id; - } - return max_id + 1; -} - - -int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd) -{ - char *curve = NULL; - char *key = NULL; - u8 *privkey = NULL; - size_t privkey_len = 0; - int ret = -1; - struct dpp_configurator *conf = NULL; - - curve = get_param(cmd, " curve="); - key = get_param(cmd, " key="); - - if (key) { - privkey_len = os_strlen(key) / 2; - privkey = os_malloc(privkey_len); - if (!privkey || - hexstr2bin(key, privkey, privkey_len) < 0) - goto fail; - } - - conf = dpp_keygen_configurator(curve, privkey, privkey_len); - if (!conf) - goto fail; - - conf->id = wpas_dpp_next_configurator_id(wpa_s); - dl_list_add(&wpa_s->dpp_configurator, &conf->list); - ret = conf->id; - conf = NULL; -fail: - os_free(curve); - str_clear_free(key); - bin_clear_free(privkey, privkey_len); - dpp_configurator_free(conf); - return ret; -} - - -static int dpp_configurator_del(struct wpa_supplicant *wpa_s, unsigned int id) -{ - struct dpp_configurator *conf, *tmp; - int found = 0; - - dl_list_for_each_safe(conf, tmp, &wpa_s->dpp_configurator, - struct dpp_configurator, list) { - if (id && conf->id != id) - continue; - found = 1; - dl_list_del(&conf->list); - dpp_configurator_free(conf); - } - - if (id == 0) - return 0; /* flush succeeds regardless of entries found */ - return found ? 0 : -1; -} - - -int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id) -{ - unsigned int id_val; - - if (os_strcmp(id, "*") == 0) { - id_val = 0; - } else { - id_val = atoi(id); - if (id_val == 0) - return -1; - } - - return dpp_configurator_del(wpa_s, id_val); -} - - int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) { struct dpp_authentication *auth; @@ -2276,11 +1916,9 @@ int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) curve = get_param(cmd, " curve="); wpas_dpp_set_testing_options(wpa_s, auth); - if (wpas_dpp_set_configurator(wpa_s, auth, cmd) == 0 && - dpp_configurator_own_config(auth, curve, 0) == 0) { - wpas_dpp_handle_config_obj(wpa_s, auth); - ret = 0; - } + if (dpp_set_configurator(wpa_s->dpp, wpa_s, auth, cmd) == 0 && + dpp_configurator_own_config(auth, curve, 0) == 0) + ret = wpas_dpp_handle_config_obj(wpa_s, auth); dpp_auth_deinit(auth); os_free(curve); @@ -2289,19 +1927,6 @@ int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) } -int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id, - char *buf, size_t buflen) -{ - struct dpp_configurator *conf; - - conf = dpp_configurator_get_id(wpa_s, id); - if (!conf) - return -1; - - return dpp_configurator_get_key(conf, buf, buflen); -} - - static void wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, @@ -2329,9 +1954,15 @@ int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct os_time now; struct wpabuf *msg; unsigned int wait_time; + const u8 *rsn; + struct wpa_ie_data ied; if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss) return 0; /* Not using DPP AKM - continue */ + rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && + !(ied.key_mgmt & WPA_KEY_MGMT_DPP)) + return 0; /* AP does not support DPP AKM - continue */ if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid)) return 0; /* PMKSA exists for DPP AKM - continue */ @@ -2444,7 +2075,7 @@ int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd) if (!pos) return -1; pos += 5; - own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + own_bi = dpp_bootstrap_get_id(wpa_s->dpp, atoi(pos)); if (!own_bi) { wpa_printf(MSG_DEBUG, "DPP: Identified bootstrap info not found"); @@ -2577,10 +2208,8 @@ int wpas_dpp_init(struct wpa_supplicant *wpa_s) sizeof(adv_proto_id), wpas_dpp_gas_req_handler, wpas_dpp_gas_status_handler, wpa_s) < 0) return -1; - dl_list_init(&wpa_s->dpp_bootstrap); - dl_list_init(&wpa_s->dpp_configurator); - wpa_s->dpp_init_done = 1; - return 0; + wpa_s->dpp = dpp_global_init(); + return wpa_s->dpp ? 0 : -1; } @@ -2595,16 +2224,20 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) wpa_s->dpp_groups_override = NULL; wpa_s->dpp_ignore_netaccesskey_mismatch = 0; #endif /* CONFIG_TESTING_OPTIONS */ - if (!wpa_s->dpp_init_done) + if (!wpa_s->dpp) return; + dpp_global_clear(wpa_s->dpp); eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); +#ifdef CONFIG_DPP2 + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL); + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = NULL; +#endif /* CONFIG_DPP2 */ offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); - dpp_bootstrap_del(wpa_s, 0); - dpp_configurator_del(wpa_s, 0); wpas_dpp_stop(wpa_s); wpas_dpp_pkex_remove(wpa_s, "*"); os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN); diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 5a4f06e2e97e..ecb7a7d684fa 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -10,12 +10,6 @@ #define DPP_SUPPLICANT_H int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id); -const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, - unsigned int id); -int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id, - char *reply, int reply_size); int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd); void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s); @@ -23,11 +17,7 @@ void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, unsigned int freq); void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len, unsigned int freq); -int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id); int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd); -int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id, - char *buf, size_t buflen); int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id); void wpas_dpp_stop(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 078de23f794f..4a9f472e84ee 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -492,6 +492,14 @@ static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_channel_info(struct wpa_supplicant *wpa_s, + struct wpa_channel_info *ci) +{ + if (wpa_s->driver->channel_info) + return wpa_s->driver->channel_info(wpa_s->drv_priv, ci); + return -1; +} + static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s, struct hostap_sta_driver_data *sta) { @@ -796,6 +804,14 @@ static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa); } +static inline int wpa_drv_set_receive_lowest_pn(struct wpa_supplicant *wpa_s, + struct receive_sa *sa) +{ + if (!wpa_s->driver->set_receive_lowest_pn) + return -1; + return wpa_s->driver->set_receive_lowest_pn(wpa_s->drv_priv, sa); +} + static inline int wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc, unsigned int conf_offset, int validation) @@ -1046,4 +1062,12 @@ wpa_drv_send_external_auth_status(struct wpa_supplicant *wpa_s, params); } +static inline int wpa_drv_set_4addr_mode(struct wpa_supplicant *wpa_s, int val) +{ + if (!wpa_s->driver->set_4addr_mode) + return -1; + return wpa_s->driver->set_4addr_mode(wpa_s->drv_priv, + wpa_s->bridge_ifname, val); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index 6548bd17b11f..3fd4ce61a1c2 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -711,7 +711,8 @@ static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx) eap = (struct eap_hdr *) (hdr + 1); eap->code = EAP_CODE_REQUEST; - eap->identifier = 0; + if (os_get_random((u8 *) &eap->identifier, sizeof(eap->identifier)) < 0) + eap->identifier = os_random() & 0xff; eap->length = htons(5); pos = (u8 *) (eap + 1); *pos = EAP_TYPE_IDENTITY; diff --git a/wpa_supplicant/eapol_test.py b/wpa_supplicant/eapol_test.py index 80e7dfcf531d..734428d29e66 100755 --- a/wpa_supplicant/eapol_test.py +++ b/wpa_supplicant/eapol_test.py @@ -136,7 +136,7 @@ def main(): results = res[i].get(False) except: results = "N/A" - print "%d: %s" % (i, results) + print("%d: %s" % (i, results)) if __name__ == "__main__": main() diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 37d429d33022..f6ec111b77b6 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2017, Jouni Malinen + * Copyright (c) 2003-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,6 +29,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/gas_server.h" +#include "common/dpp.h" #include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" @@ -293,6 +294,13 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return; + if (os_reltime_initialized(&wpa_s->session_start)) { + os_reltime_age(&wpa_s->session_start, &wpa_s->session_length); + wpa_s->session_start.sec = 0; + wpa_s->session_start.usec = 0; + wpas_notify_session_length(wpa_s); + } + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); @@ -324,6 +332,9 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk)); #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->ieee80211ac = 0; + + if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) + wpa_s->enabled_4addr_mode = 0; } @@ -549,6 +560,10 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, " skip RSN IE - parse failed"); break; } + if (!ie.has_pairwise) + ie.pairwise_cipher = wpa_default_rsn_cipher(bss->freq); + if (!ie.has_group) + ie.group_cipher = wpa_default_rsn_cipher(bss->freq); if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) @@ -1335,10 +1350,10 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } - if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) { + if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, - " skip - MBO retry delay has not passed yet"); + " skip - AP temporarily disallowed"); continue; } #ifdef CONFIG_TESTING_OPTIONS @@ -1889,7 +1904,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; - if (own_request && + if (own_request && data && wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0) goto scan_work_done; @@ -2267,6 +2282,57 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + const u8 *map_sub_elem, *pos; + size_t len; + + if (!wpa_s->current_ssid || + !wpa_s->current_ssid->multi_ap_backhaul_sta || + !ies || + ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + + if (!elems.multi_ap || elems.multi_ap_len < 7) { + wpa_printf(MSG_INFO, "AP doesn't support Multi-AP protocol"); + goto fail; + } + + pos = elems.multi_ap + 4; + len = elems.multi_ap_len - 4; + + map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE); + if (!map_sub_elem || map_sub_elem[1] < 1) { + wpa_printf(MSG_INFO, "invalid Multi-AP sub elem type"); + goto fail; + } + + if (!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS)) { + if ((map_sub_elem[2] & MULTI_AP_FRONTHAUL_BSS) && + wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + wpa_printf(MSG_INFO, + "WPS active, accepting fronthaul-only BSS"); + /* Don't set 4addr mode in this case, so just return */ + return; + } + wpa_printf(MSG_INFO, "AP doesn't support backhaul BSS"); + goto fail; + } + + if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) { + wpa_printf(MSG_ERROR, "Failed to set 4addr mode"); + goto fail; + } + wpa_s->enabled_4addr_mode = 1; + return; + +fail: + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); +} + + #ifdef CONFIG_FST static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len) @@ -2343,6 +2409,9 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, get_ie(data->assoc_info.resp_ies, data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) wpa_s->ieee80211ac = 1; + + multi_ap_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", @@ -2352,6 +2421,26 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq); + wpa_s->connection_set = 0; + if (data->assoc_info.req_ies && data->assoc_info.resp_ies) { + struct ieee802_11_elems req_elems, resp_elems; + + if (ieee802_11_parse_elems(data->assoc_info.req_ies, + data->assoc_info.req_ies_len, + &req_elems, 0) != ParseFailed && + ieee802_11_parse_elems(data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &resp_elems, 0) != ParseFailed) { + wpa_s->connection_set = 1; + wpa_s->connection_ht = req_elems.ht_capabilities && + resp_elems.ht_capabilities; + wpa_s->connection_vht = req_elems.vht_capabilities && + resp_elems.vht_capabilities; + wpa_s->connection_he = req_elems.he_capabilities && + resp_elems.he_capabilities; + } + } + p = data->assoc_info.req_ies; l = data->assoc_info.req_ies_len; @@ -2410,6 +2499,28 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + wpa_sm_set_dpp_z(wpa_s->wpa, NULL); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->dpp_pfs) { + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &elems, 0) == ParseFailed || + !elems.owe_dh) + goto no_pfs; + if (dpp_pfs_process(wpa_s->dpp_pfs, elems.owe_dh, + elems.owe_dh_len) < 0) { + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_UNSPECIFIED); + return -1; + } + + wpa_sm_set_dpp_z(wpa_s->wpa, wpa_s->dpp_pfs->secret); + } +no_pfs: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { @@ -2648,6 +2759,16 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + if (os_reltime_initialized(&wpa_s->session_start)) { + os_reltime_age(&wpa_s->session_start, + &wpa_s->session_length); + wpa_s->session_start.sec = 0; + wpa_s->session_start.usec = 0; + wpas_notify_session_length(wpa_s); + } else { + wpas_notify_auth_changed(wpa_s); + os_get_reltime(&wpa_s->session_start); + } wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); new_bss = 1; @@ -2738,25 +2859,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, } wpa_supplicant_cancel_scan(wpa_s); - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && - wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { - /* - * We are done; the driver will take care of RSN 4-way - * handshake. - */ - wpa_supplicant_cancel_auth_timeout(wpa_s); - wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); - eapol_sm_notify_portValid(wpa_s->eapol, TRUE); - eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); - } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && - wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { - /* - * The driver will take care of RSN 4-way handshake, so we need - * to allow EAPOL supplicant to complete its work without - * waiting for WPA supplicant. - */ - eapol_sm_notify_portValid(wpa_s->eapol, TRUE); - } else if (ft_completed) { + if (ft_completed) { /* * FT protocol completed - make sure EAPOL state machine ends * up in authenticated. @@ -2765,6 +2868,24 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && + wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + /* + * We are done; the driver will take care of RSN 4-way + * handshake. + */ + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) && + wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + /* + * The driver will take care of RSN 4-way handshake, so we need + * to allow EAPOL supplicant to complete its work without + * waiting for WPA supplicant. + */ + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); } wpa_s->last_eapol_matches_bssid = 0; @@ -3010,7 +3131,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, !disallowed_ssid(wpa_s, fast_reconnect->ssid, fast_reconnect->ssid_len) && !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) && - !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) { + !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect)) { #ifndef CONFIG_NO_SCAN_PROCESSING wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); if (wpa_supplicant_connect(wpa_s, fast_reconnect, @@ -3583,8 +3704,8 @@ static const char * reg_type_str(enum reg_type type) } -static void wpa_supplicant_update_channel_list( - struct wpa_supplicant *wpa_s, struct channel_list_changed *info) +void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, + struct channel_list_changed *info) { struct wpa_supplicant *ifs; u8 dfs_domain; @@ -3598,10 +3719,13 @@ static void wpa_supplicant_update_channel_list( for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent) ; - wpa_msg(ifs, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", - reg_init_str(info->initiator), reg_type_str(info->type), - info->alpha2[0] ? " alpha2=" : "", - info->alpha2[0] ? info->alpha2 : ""); + if (info) { + wpa_msg(ifs, MSG_INFO, + WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", + reg_init_str(info->initiator), reg_type_str(info->type), + info->alpha2[0] ? " alpha2=" : "", + info->alpha2[0] ? info->alpha2 : ""); + } if (wpa_s->drv_priv == NULL) return; /* Ignore event during drv initialization */ @@ -3840,7 +3964,7 @@ static void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { #if defined(NEED_AP_MLME) && defined(CONFIG_AP) - if (wpa_s->ap_iface) { + if (wpa_s->ap_iface || wpa_s->ifmsh) { wpas_ap_event_dfs_cac_started(wpa_s, radar); } else #endif /* NEED_AP_MLME && CONFIG_AP */ @@ -3861,7 +3985,7 @@ static void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { #if defined(NEED_AP_MLME) && defined(CONFIG_AP) - if (wpa_s->ap_iface) { + if (wpa_s->ap_iface || wpa_s->ifmsh) { wpas_ap_event_dfs_cac_finished(wpa_s, radar); } else #endif /* NEED_AP_MLME && CONFIG_AP */ @@ -3877,7 +4001,7 @@ static void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, struct dfs_event *radar) { #if defined(NEED_AP_MLME) && defined(CONFIG_AP) - if (wpa_s->ap_iface) { + if (wpa_s->ap_iface || wpa_s->ifmsh) { wpas_ap_event_dfs_cac_aborted(wpa_s, radar); } else #endif /* NEED_AP_MLME && CONFIG_AP */ @@ -3984,6 +4108,32 @@ static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_MBO + if (data->assoc_reject.status_code == + WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS && + wpa_s->current_bss && data->assoc_reject.bssid && + data->assoc_reject.resp_ies) { + const u8 *rssi_rej; + + rssi_rej = mbo_get_attr_from_ies( + data->assoc_reject.resp_ies, + data->assoc_reject.resp_ies_len, + OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT); + if (rssi_rej && rssi_rej[1] == 2) { + wpa_printf(MSG_DEBUG, + "OCE: RSSI-based association rejection from " + MACSTR " (Delta RSSI: %u, Retry Delay: %u)", + MAC2STR(data->assoc_reject.bssid), + rssi_rej[2], rssi_rej[3]); + wpa_bss_tmp_disallow(wpa_s, + data->assoc_reject.bssid, + rssi_rej[3], + rssi_rej[2] + + wpa_s->current_bss->level); + } + } +#endif /* CONFIG_MBO */ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { sme_event_assoc_reject(wpa_s, data); return; @@ -4070,6 +4220,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, "FST: MB IEs updated from auth IE"); #endif /* CONFIG_FST */ sme_event_auth(wpa_s, data); + wpa_s->auth_status_code = data->auth.status_code; + wpas_notify_auth_status_code(wpa_s); break; case EVENT_ASSOC: #ifdef CONFIG_TESTING_OPTIONS @@ -4328,6 +4480,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #ifdef CONFIG_AP if (wpa_s->current_ssid->mode == WPAS_MODE_AP || wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO || + wpa_s->current_ssid->mode == WPAS_MODE_MESH || wpa_s->current_ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, @@ -4339,6 +4492,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #endif /* CONFIG_AP */ +#ifdef CONFIG_IEEE80211W + sme_event_ch_switch(wpa_s); +#endif /* CONFIG_IEEE80211W */ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS); wnm_clear_coloc_intf_reporting(wpa_s); break; @@ -4707,7 +4863,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS - wpas_wps_start_pbc(wpa_s, NULL, 0); + wpas_wps_start_pbc(wpa_s, NULL, 0, 0); #endif /* CONFIG_WPS */ break; case EVENT_AVOID_FREQUENCIES: diff --git a/wpa_supplicant/examples/dbus-listen-preq.py b/wpa_supplicant/examples/dbus-listen-preq.py index 5ac9859f7b72..337519f4e927 100755 --- a/wpa_supplicant/examples/dbus-listen-preq.py +++ b/wpa_supplicant/examples/dbus-listen-preq.py @@ -1,5 +1,6 @@ #!/usr/bin/python +from __future__ import print_function import dbus import sys import time @@ -12,21 +13,24 @@ WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface" def usage(): - print "Usage: %s " % sys.argv[0] - print "Press Ctrl-C to stop" + print("Usage: %s " % sys.argv[0]) + print("Press Ctrl-C to stop") def ProbeRequest(args): if 'addr' in args: - print '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']), + print('%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']), + end=' ') if 'dst' in args: - print '-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']), + print('-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']), + end=' ') if 'bssid' in args: - print '(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']), + print('(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']), + end=' ') if 'signal' in args: - print 'signal:%d' % args['signal'], + print('signal:%d' % args['signal'], end=' ') if 'ies' in args: - print 'have IEs (%d bytes)' % len(args['ies']), - print '' + print('have IEs (%d bytes)' % len(args['ies']), end=' ') + print('') if __name__ == "__main__": global bus diff --git a/wpa_supplicant/examples/dpp-qrcode.py b/wpa_supplicant/examples/dpp-qrcode.py index e2a00c910812..b468d15cf9cd 100755 --- a/wpa_supplicant/examples/dpp-qrcode.py +++ b/wpa_supplicant/examples/dpp-qrcode.py @@ -24,19 +24,19 @@ def wpas_connect(): if os.path.isdir(wpas_ctrl): try: ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] - except OSError, error: - print "Could not find wpa_supplicant: ", error + except OSError as error: + print("Could not find wpa_supplicant: ", error) return None if len(ifaces) < 1: - print "No wpa_supplicant control interface found" + print("No wpa_supplicant control interface found") return None for ctrl in ifaces: try: wpas = wpaspy.Ctrl(ctrl) return wpas - except Exception, e: + except Exception as e: pass return None @@ -55,27 +55,27 @@ def dpp_logcat(): continue if not uri.startswith('DPP:'): continue - print "Found DPP bootstrap info URI:" - print uri + print("Found DPP bootstrap info URI:") + print(uri) wpas = wpas_connect() if not wpas: - print "Could not connect to wpa_supplicant" - print + print("Could not connect to wpa_supplicant") + print('') continue res = wpas.request("DPP_QR_CODE " + uri); try: id = int(res) except ValueError: - print "QR Code URI rejected" + print("QR Code URI rejected") continue - print "QR Code URI accepted - ID=%d" % id - print wpas.request("DPP_BOOTSTRAP_INFO %d" % id) + print("QR Code URI accepted - ID=%d" % id) + print(wpas.request("DPP_BOOTSTRAP_INFO %d" % id)) del wpas def dpp_display(curve): wpas = wpas_connect() if not wpas: - print "Could not connect to wpa_supplicant" + print("Could not connect to wpa_supplicant") return res = wpas.request("STATUS") addr = None @@ -93,18 +93,18 @@ def dpp_display(curve): try: id = int(res) except ValueError: - print "Failed to generate bootstrap info URI" + print("Failed to generate bootstrap info URI") return - print "Bootstrap information - ID=%d" % id - print wpas.request("DPP_BOOTSTRAP_INFO %d" % id) + print("Bootstrap information - ID=%d" % id) + print(wpas.request("DPP_BOOTSTRAP_INFO %d" % id)) uri = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id) - print uri - print "ID=%d" % id + print(uri) + print("ID=%d" % id) qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M, border=3) qr.add_data(uri, optimize=5) qr.print_ascii(tty=True) - print "ID=%d" % id + print("ID=%d" % id) del wpas def main(): diff --git a/wpa_supplicant/examples/p2p-nfc.py b/wpa_supplicant/examples/p2p-nfc.py index 91eba28908ed..889ac8bff155 100755 --- a/wpa_supplicant/examples/p2p-nfc.py +++ b/wpa_supplicant/examples/p2p-nfc.py @@ -37,7 +37,7 @@ success_file = None def summary(txt): - print txt + print(txt) if summary_file: with open(summary_file, 'a') as f: f.write(txt + "\n") @@ -53,12 +53,12 @@ def wpas_connect(): if os.path.isdir(wpas_ctrl): try: ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] - except OSError, error: - print "Could not find wpa_supplicant: ", error + except OSError as error: + print("Could not find wpa_supplicant: ", error) return None if len(ifaces) < 1: - print "No wpa_supplicant control interface found" + print("No wpa_supplicant control interface found") return None for ctrl in ifaces: @@ -66,10 +66,10 @@ def wpas_connect(): if ifname not in ctrl: continue try: - print "Trying to use control interface " + ctrl + print("Trying to use control interface " + ctrl) wpas = wpaspy.Ctrl(ctrl) return wpas - except Exception, e: + except Exception as e: pass return None @@ -160,30 +160,30 @@ def p2p_handover_client(llc): if (data == None): summary("Could not get handover request carrier record from wpa_supplicant") return - print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") + print("Handover request carrier record from wpa_supplicant: " + data.encode("hex")) datamsg = nfc.ndef.Message(data) message.add_carrier(datamsg[0], "active", datamsg[1:]) global include_wps_req if include_wps_req: - print "Handover request (pre-WPS):" + print("Handover request (pre-WPS):") try: - print message.pretty() - except Exception, e: - print e + print(message.pretty()) + except Exception as e: + print(e) data = wpas_get_handover_req_wps() if data: - print "Add WPS request in addition to P2P" + print("Add WPS request in addition to P2P") datamsg = nfc.ndef.Message(data) message.add_carrier(datamsg[0], "active", datamsg[1:]) - print "Handover request:" + print("Handover request:") try: - print message.pretty() - except Exception, e: - print e - print str(message).encode("hex") + print(message.pretty()) + except Exception as e: + print(e) + print(str(message).encode("hex")) client = nfc.handover.HandoverClient(llc) try: @@ -194,7 +194,7 @@ def p2p_handover_client(llc): summary("Handover connection refused") client.close() return - except Exception, e: + except Exception as e: summary("Other exception: " + str(e)) client.close() return @@ -217,41 +217,41 @@ def p2p_handover_client(llc): client.close() return - print "Received message" + print("Received message") try: - print message.pretty() - except Exception, e: - print e - print str(message).encode("hex") + print(message.pretty()) + except Exception as e: + print(e) + print(str(message).encode("hex")) message = nfc.ndef.HandoverSelectMessage(message) summary("Handover select received") try: - print message.pretty() - except Exception, e: - print e + print(message.pretty()) + except Exception as e: + print(e) for carrier in message.carriers: - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.p2p": - print "P2P carrier type match - send to wpa_supplicant" + print("P2P carrier type match - send to wpa_supplicant") if "OK" in wpas_report_handover(data, carrier.record, "INIT"): success_report("P2P handover reported successfully (initiator)") else: summary("P2P handover report rejected") break - print "Remove peer" + print("Remove peer") client.close() - print "Done with handover" + print("Done with handover") global only_one if only_one: - print "only_one -> stop loop" + print("only_one -> stop loop") global continue_loop continue_loop = False global no_wait if no_wait: - print "Trying to exit.." + print("Trying to exit..") global terminate_now terminate_now = True @@ -283,33 +283,33 @@ def _process_request(self, request): def process_request(self, request): self.ho_server_processing = True clear_raw_mode() - print "HandoverServer - request received" + print("HandoverServer - request received") try: - print "Parsed handover request: " + request.pretty() - except Exception, e: - print e + print("Parsed handover request: " + request.pretty()) + except Exception as e: + print(e) sel = nfc.ndef.HandoverSelectMessage(version="1.2") found = False for carrier in request.carriers: - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.p2p": - print "P2P carrier type match - add P2P carrier record" + print("P2P carrier type match - add P2P carrier record") found = True self.received_carrier = carrier.record - print "Carrier record:" + print("Carrier record:") try: - print carrier.record.pretty() - except Exception, e: - print e + print(carrier.record.pretty()) + except Exception as e: + print(e) data = wpas_get_handover_sel() if data is None: - print "Could not get handover select carrier record from wpa_supplicant" + print("Could not get handover select carrier record from wpa_supplicant") continue - print "Handover select carrier record from wpa_supplicant:" - print data.encode("hex") + print("Handover select carrier record from wpa_supplicant:") + print(data.encode("hex")) self.sent_carrier = data if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"): success_report("P2P handover reported successfully (responder)") @@ -324,22 +324,22 @@ def process_request(self, request): for carrier in request.carriers: if found: break - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.wsc": - print "WSC carrier type match - add WSC carrier record" + print("WSC carrier type match - add WSC carrier record") found = True self.received_carrier = carrier.record - print "Carrier record:" + print("Carrier record:") try: - print carrier.record.pretty() - except Exception, e: - print e + print(carrier.record.pretty()) + except Exception as e: + print(e) data = wpas_get_handover_sel_wps() if data is None: - print "Could not get handover select carrier record from wpa_supplicant" + print("Could not get handover select carrier record from wpa_supplicant") continue - print "Handover select carrier record from wpa_supplicant:" - print data.encode("hex") + print("Handover select carrier record from wpa_supplicant:") + print(data.encode("hex")) self.sent_carrier = data if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"): success_report("WSC handover reported successfully") @@ -352,12 +352,12 @@ def process_request(self, request): found = True break - print "Handover select:" + print("Handover select:") try: - print sel.pretty() - except Exception, e: - print e - print str(sel).encode("hex") + print(sel.pretty()) + except Exception as e: + print(e) + print(str(sel).encode("hex")) summary("Sending handover select") self.success = True @@ -396,7 +396,7 @@ def p2p_tag_read(tag): success = False if len(tag.ndef.message): for record in tag.ndef.message: - print "record type " + record.type + print("record type " + record.type) if record.type == "application/vnd.wfa.wsc": summary("WPS tag - send to wpa_supplicant") success = wpas_tag_read(tag.ndef.message) @@ -419,7 +419,7 @@ def rdwr_connected_p2p_write(tag): global p2p_sel_data tag.ndef.message = str(p2p_sel_data) success_report("Tag write succeeded") - print "Done - remove tag" + print("Done - remove tag") global only_one if only_one: global continue_loop @@ -428,7 +428,7 @@ def rdwr_connected_p2p_write(tag): return p2p_sel_wait_remove def wps_write_p2p_handover_sel(clf, wait_remove=True): - print "Write P2P handover select" + print("Write P2P handover select") data = wpas_get_handover_sel(tag=True) if (data == None): summary("Could not get P2P handover select from wpa_supplicant") @@ -440,14 +440,14 @@ def wps_write_p2p_handover_sel(clf, wait_remove=True): p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2") message = nfc.ndef.Message(data); p2p_sel_data.add_carrier(message[0], "active", message[1:]) - print "Handover select:" + print("Handover select:") try: - print p2p_sel_data.pretty() - except Exception, e: - print e - print str(p2p_sel_data).encode("hex") + print(p2p_sel_data.pretty()) + except Exception as e: + print(e) + print(str(p2p_sel_data).encode("hex")) - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write}) @@ -456,11 +456,11 @@ def rdwr_connected(tag): summary("Tag connected: " + str(tag)) if tag.ndef: - print "NDEF tag: " + tag.type + print("NDEF tag: " + tag.type) try: - print tag.ndef.message.pretty() - except Exception, e: - print e + print(tag.ndef.message.pretty()) + except Exception as e: + print(e) success = p2p_tag_read(tag) if only_one and success: global continue_loop @@ -475,15 +475,15 @@ def rdwr_connected(tag): def llcp_worker(llc): global init_on_touch if init_on_touch: - print "Starting handover client" + print("Starting handover client") p2p_handover_client(llc) return global no_input if no_input: - print "Wait for handover to complete" + print("Wait for handover to complete") else: - print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)" + print("Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)") global srv global wait_connection while not wait_connection and srv.sent_carrier is None: @@ -506,21 +506,21 @@ def llcp_worker(llc): else: continue clear_raw_mode() - print "Starting handover client" + print("Starting handover client") p2p_handover_client(llc) return clear_raw_mode() - print "Exiting llcp_worker thread" + print("Exiting llcp_worker thread") def llcp_startup(clf, llc): - print "Start LLCP server" + print("Start LLCP server") global srv srv = HandoverServer(llc) return llc def llcp_connected(llc): - print "P2P LLCP connected" + print("P2P LLCP connected") global wait_connection wait_connection = False global init_on_touch @@ -587,7 +587,7 @@ def main(): if args.ifname: global ifname ifname = args.ifname - print "Selected ifname " + ifname + print("Selected ifname " + ifname) if args.no_wps_req: global include_wps_req @@ -610,7 +610,7 @@ def main(): try: if not clf.open("usb"): - print "Could not open connection with an NFC device" + print("Could not open connection with an NFC device") raise SystemExit if args.command == "write-p2p-sel": @@ -619,7 +619,7 @@ def main(): global continue_loop while continue_loop: - print "Waiting for a tag or peer to be touched" + print("Waiting for a tag or peer to be touched") wait_connection = True try: if args.tag_read_only: @@ -636,8 +636,8 @@ def main(): 'on-connect': llcp_connected}, terminate=terminate_loop): break - except Exception, e: - print "clf.connect failed" + except Exception as e: + print("clf.connect failed") global srv if only_one and srv and srv.success: diff --git a/wpa_supplicant/examples/p2p/p2p_connect.py b/wpa_supplicant/examples/p2p/p2p_connect.py index 59b0a9d36462..6e3d94e20ca3 100644 --- a/wpa_supplicant/examples/p2p/p2p_connect.py +++ b/wpa_supplicant/examples/p2p/p2p_connect.py @@ -13,40 +13,40 @@ def usage(): - print "Usage:" - print " %s -i -m \ " \ - % sys.argv[0] - print " -a [-p ] [-g ] \ " - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -m = wps method" - print " -a = peer address" - print " -p = pin number (8 digits)" - print " -g = group owner intent" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0] + print("Usage:") + print(" %s -i -m \ " \ + % sys.argv[0]) + print(" -a [-p ] [-g ] \ ") + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -m = wps method") + print(" -a = peer address") + print(" -p = pin number (8 digits)") + print(" -g = group owner intent") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0]) # Required Signals def GONegotiationSuccess(status): - print "Go Negotiation Success" + print("Go Negotiation Success") def GONegotiationFailure(status): - print 'Go Negotiation Failed. Status:' - print format(status) + print('Go Negotiation Failed. Status:') + print(format(status)) os._exit(0) def GroupStarted(properties): if properties.has_key("group_object"): - print 'Group Formation Complete %s' \ - % properties["group_object"] + print('Group Formation Complete %s' \ + % properties["group_object"]) os._exit(0) def WpsFailure(status, etc): - print "WPS Authentication Failure".format(status) - print etc + print("WPS Authentication Failure".format(status)) + print(etc) os._exit(0) class P2P_Connect(): @@ -118,7 +118,7 @@ def __init__(self,ifname,wpas_dbus_interface,addr, {'Ifname': ifname, 'Driver': 'test'}) time.sleep(1) - except dbus.DBusException, exc: + except dbus.DBusException as exc: if not str(exc).startswith( self.wpas_dbus_interface + \ ".InterfaceExists:"): @@ -157,12 +157,12 @@ def constructArguements(self): if (self.pin != None): self.p2p_connect_arguements.update({'pin':self.pin}) else: - print "Error:\n Pin required for wps_method=display" + print("Error:\n Pin required for wps_method=display") usage() quit() if (self.go_intent != None and int(self.go_intent) != 15): - print "go_intent overwritten to 15" + print("go_intent overwritten to 15") self.go_intent = '15' @@ -171,14 +171,14 @@ def constructArguements(self): if (self.pin != None): self.p2p_connect_arguements.update({'pin':self.pin}) else: - print "Error:\n Pin required for wps_method=keypad" + print("Error:\n Pin required for wps_method=keypad") usage() quit() if (self.go_intent != None and int(self.go_intent) == 15): error = "Error :\n Group Owner intent cannot be" + \ " 15 for wps_method=keypad" - print error + print(error) usage() quit() @@ -186,15 +186,15 @@ def constructArguements(self): # for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad elif (self.wps_method == 'pin'): if (self.pin != None): - print "pin ignored" + print("pin ignored") # No pin is required for pbc so it is ignored elif (self.wps_method == 'pbc'): if (self.pin != None): - print "pin ignored" + print("pin ignored") else: - print "Error:\n wps_method not supported or does not exist" + print("Error:\n wps_method not supported or does not exist") usage() quit() @@ -209,12 +209,12 @@ def run(self): result_pin = self.p2p_interface.Connect( self.p2p_connect_arguements) - except dbus.DBusException, exc: + except dbus.DBusException as exc: raise exc if (self.wps_method == 'pin' and \ not self.p2p_connect_arguements.has_key('pin') ): - print "Connect return with pin value of %d " % int(result_pin) + print("Connect return with pin value of %d " % int(result_pin)) gobject.MainLoop().run() if __name__ == "__main__": @@ -268,19 +268,19 @@ def run(self): # Required Arguements check if (interface_name == None or wps_method == None or addr == None): - print "Error:\n Required arguements not specified" + print("Error:\n Required arguements not specified") usage() quit() # Group Owner Intent Check if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ): - print "Error:\n Group Owner Intent must be between 0 and 15 inclusive" + print("Error:\n Group Owner Intent must be between 0 and 15 inclusive") usage() quit() # Pin Check if (pin != None and len(pin) != 8): - print "Error:\n Pin is not 8 digits" + print("Error:\n Pin is not 8 digits") usage() quit() @@ -289,7 +289,7 @@ def run(self): addr,pin,wps_method,go_intent) except: - print "Error:\n Invalid Arguements" + print("Error:\n Invalid Arguements") usage() quit() diff --git a/wpa_supplicant/examples/p2p/p2p_disconnect.py b/wpa_supplicant/examples/p2p/p2p_disconnect.py index c3e39b3de285..85b5a8b39f5e 100644 --- a/wpa_supplicant/examples/p2p/p2p_disconnect.py +++ b/wpa_supplicant/examples/p2p/p2p_disconnect.py @@ -12,19 +12,19 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i \ " \ - % sys.argv[0] - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i p2p-wlan0-0" % sys.argv[0] + print("Usage:") + print(" %s -i \ " \ + % sys.argv[0]) + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i p2p-wlan0-0" % sys.argv[0]) # Required Signals def GroupFinished(status, etc): - print "Disconnected" + print("Disconnected") os._exit(0) class P2P_Disconnect (threading.Thread): @@ -81,10 +81,10 @@ def __init__(self,interface_name,wpas_dbus_interface,timeout): try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -142,7 +142,7 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() @@ -152,7 +152,7 @@ def run(self): wpas_dbus_interface,timeout) except: - print "Error:\n Invalid wpas_dbus_interface" + print("Error:\n Invalid wpas_dbus_interface") usage() quit() @@ -165,5 +165,5 @@ def run(self): except: pass - print "Disconnect timed out" + print("Disconnect timed out") quit() diff --git a/wpa_supplicant/examples/p2p/p2p_find.py b/wpa_supplicant/examples/p2p/p2p_find.py index 973d46ab0f4b..e2df52896991 100644 --- a/wpa_supplicant/examples/p2p/p2p_find.py +++ b/wpa_supplicant/examples/p2p/p2p_find.py @@ -13,23 +13,23 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i [-t ] \ " \ - % sys.argv[0] - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -t = timeout = 0s (infinite)" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i wlan0 -t 10" % sys.argv[0] + print("Usage:") + print(" %s -i [-t ] \ " \ + % sys.argv[0]) + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -t = timeout = 0s (infinite)") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i wlan0 -t 10" % sys.argv[0]) # Required Signals def deviceFound(devicepath): - print "Device found: %s" % (devicepath) + print("Device found: %s" % (devicepath)) def deviceLost(devicepath): - print "Device lost: %s" % (devicepath) + print("Device lost: %s" % (devicepath)) class P2P_Find (threading.Thread): # Needed Variables @@ -85,10 +85,10 @@ def __init__(self,interface_name,wpas_dbus_interface,timeout): try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -150,7 +150,7 @@ def run(self): if ( int(value) >= 0): timeout = value else: - print "Error:\n Timeout cannot be negative" + print("Error:\n Timeout cannot be negative") usage() quit() # Dbus interface @@ -161,7 +161,7 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() @@ -170,7 +170,7 @@ def run(self): p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout) except: - print "Error:\n Invalid wpas_dbus_interface" + print("Error:\n Invalid wpas_dbus_interface") usage() quit() diff --git a/wpa_supplicant/examples/p2p/p2p_flush.py b/wpa_supplicant/examples/p2p/p2p_flush.py index ff8509d6053d..42fc7a3e915a 100644 --- a/wpa_supplicant/examples/p2p/p2p_flush.py +++ b/wpa_supplicant/examples/p2p/p2p_flush.py @@ -13,19 +13,19 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i \ " \ - % sys.argv[0] - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i wlan0" % sys.argv[0] + print("Usage:") + print(" %s -i \ " \ + % sys.argv[0]) + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i wlan0" % sys.argv[0]) # Required Signals\ def deviceLost(devicepath): - print "Device lost: %s" % (devicepath) + print("Device lost: %s" % (devicepath)) class P2P_Flush (threading.Thread): # Needed Variables @@ -81,10 +81,10 @@ def __init__(self,interface_name,wpas_dbus_interface,timeout): try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -142,7 +142,7 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() @@ -151,7 +151,7 @@ def run(self): p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout) except: - print "Error:\n Invalid wpas_dbus_interface" + print("Error:\n Invalid wpas_dbus_interface") usage() quit() @@ -164,5 +164,5 @@ def run(self): except: pass - print "p2p_flush complete" + print("p2p_flush complete") quit() diff --git a/wpa_supplicant/examples/p2p/p2p_group_add.py b/wpa_supplicant/examples/p2p/p2p_group_add.py index 5c8fdafdfd9a..6d408218a25e 100644 --- a/wpa_supplicant/examples/p2p/p2p_group_add.py +++ b/wpa_supplicant/examples/p2p/p2p_group_add.py @@ -11,30 +11,30 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i [-p ] \ " \ - % sys.argv[0] - print " [-f ] [-o ] \ " - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -p = persistant group = 0 (0=false, 1=true)" - print " -f = frequency" - print " -o = persistent group object path" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i wlan0" % sys.argv[0] + print("Usage:") + print(" %s -i [-p ] \ " \ + % sys.argv[0]) + print(" [-f ] [-o ] \ ") + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -p = persistant group = 0 (0=false, 1=true)") + print(" -f = frequency") + print(" -o = persistent group object path") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i wlan0" % sys.argv[0]) # Required Signals def GroupStarted(properties): if properties.has_key("group_object"): - print 'Group Formation Complete %s' \ - % properties["group_object"] + print('Group Formation Complete %s' \ + % properties["group_object"]) os._exit(0) def WpsFailure(status, etc): - print "WPS Authentication Failure".format(status) - print etc + print("WPS Authentication Failure".format(status)) + print(etc) os._exit(0) class P2P_Group_Add (threading.Thread): @@ -99,10 +99,10 @@ def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency, try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -127,7 +127,7 @@ def constructArguements(self): if (int(self.frequency) > 0): self.P2PDictionary.update({'frequency':int(self.frequency)}) else: - print "Error:\n Frequency must be greater than 0" + print("Error:\n Frequency must be greater than 0") usage() os._exit(0) @@ -141,7 +141,7 @@ def run(self): self.p2p_interface.GroupAdd(self.P2PDictionary) except: - print "Error:\n Could not preform group add" + print("Error:\n Could not preform group add") usage() os._exit(0) @@ -188,7 +188,7 @@ def run(self): elif (value == '1'): persistent = True else: - print "Error:\n Persistent can only be 1 or 0" + print("Error:\n Persistent can only be 1 or 0") usage() os._exit(0) # Frequency @@ -205,7 +205,7 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() @@ -213,10 +213,10 @@ def run(self): p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface, persistent,frequency,persistent_group_object) except: - print "Error:\n Invalid Arguements" + print("Error:\n Invalid Arguements") p2p_group_add_test.constructArguements() p2p_group_add_test.start() time.sleep(5) - print "Error:\n Group formation timed out" + print("Error:\n Group formation timed out") os._exit(0) diff --git a/wpa_supplicant/examples/p2p/p2p_invite.py b/wpa_supplicant/examples/p2p/p2p_invite.py index 6deb397eca83..341dcd0a983d 100644 --- a/wpa_supplicant/examples/p2p/p2p_invite.py +++ b/wpa_supplicant/examples/p2p/p2p_invite.py @@ -11,29 +11,29 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i -a \ " \ - % sys.argv[0] - print " [-o ] [-w ]" - print "Options:" - print " -i = interface name" - print " -a = address of peer" - print " -o = persistent group object path" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0] + print("Usage:") + print(" %s -i -a \ " \ + % sys.argv[0]) + print(" [-o ] [-w ]") + print("Options:") + print(" -i = interface name") + print(" -a = address of peer") + print(" -o = persistent group object path") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0]) # Required Signals def InvitationResult(invite_result): - print "Inviation Result signal :" + print("Inviation Result signal :") status = invite_result['status'] - print "status = ", status + print("status = ", status) if invite_result.has_key('BSSID'): bssid = invite_result['BSSID'] - print "BSSID = ", hex(bssid[0]) , ":" , \ + print("BSSID = ", hex(bssid[0]) , ":" , \ hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \ hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \ - hex(bssid[5]) + hex(bssid[5])) os._exit(0) class P2P_Invite (threading.Thread): @@ -96,10 +96,10 @@ def __init__(self,interface_name,wpas_dbus_interface,addr, try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -127,7 +127,7 @@ def run(self): self.p2p_interface.Invite(self.P2PDictionary) except: - print "Error:\n Invalid Arguements" + print("Error:\n Invalid Arguements") usage() os._exit(0) @@ -176,12 +176,12 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() if (addr == None): - print "Error:\n peer address is required" + print("Error:\n peer address is required") usage() quit() @@ -190,12 +190,12 @@ def run(self): P2P_Invite(interface_name,wpas_dbus_interface, addr,persistent_group_object) except: - print "Error:\n Invalid Arguements" + print("Error:\n Invalid Arguements") usage() os._exit(1) p2p_invite_test.constructArguements() p2p_invite_test.start() time.sleep(10) - print "Error:\n p2p_invite timed out" + print("Error:\n p2p_invite timed out") os._exit(0) diff --git a/wpa_supplicant/examples/p2p/p2p_listen.py b/wpa_supplicant/examples/p2p/p2p_listen.py index bb3c1e49d5f1..b0837d9df5d2 100644 --- a/wpa_supplicant/examples/p2p/p2p_listen.py +++ b/wpa_supplicant/examples/p2p/p2p_listen.py @@ -13,20 +13,20 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i [-t ] \ " \ - % sys.argv[0] - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -t = timeout = 0s (infinite)" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i wlan0 -t 5" % sys.argv[0] + print("Usage:") + print(" %s -i [-t ] \ " \ + % sys.argv[0]) + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -t = timeout = 0s (infinite)") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i wlan0 -t 5" % sys.argv[0]) # Required Signals def p2pStateChange(status): - print status + print(status) class P2P_Listen(threading.Thread): # Needed Variables @@ -82,10 +82,10 @@ def __init__(self,interface_name,wpas_dbus_interface,timeout): try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -140,7 +140,7 @@ def run(self): if ( int(value) >= 0): timeout = value else: - print "Error:\n Timeout cannot be negative" + print("Error:\n Timeout cannot be negative") usage() quit() # Dbus interface @@ -151,7 +151,7 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() @@ -160,7 +160,7 @@ def run(self): p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout) except: - print "Error:\n Invalid wpas_dbus_interface" + print("Error:\n Invalid wpas_dbus_interface") usage() quit() diff --git a/wpa_supplicant/examples/p2p/p2p_stop_find.py b/wpa_supplicant/examples/p2p/p2p_stop_find.py index f6c03b027228..bdb4c0e3221d 100644 --- a/wpa_supplicant/examples/p2p/p2p_stop_find.py +++ b/wpa_supplicant/examples/p2p/p2p_stop_find.py @@ -11,22 +11,22 @@ from dbus.mainloop.glib import DBusGMainLoop def usage(): - print "Usage:" - print " %s -i \ " \ - % sys.argv[0] - print " [-w ]" - print "Options:" - print " -i = interface name" - print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" - print "Example:" - print " %s -i wlan0" % sys.argv[0] + print("Usage:") + print(" %s -i \ " \ + % sys.argv[0]) + print(" [-w ]") + print("Options:") + print(" -i = interface name") + print(" -w = wpas dbus interface = fi.w1.wpa_supplicant1") + print("Example:") + print(" %s -i wlan0" % sys.argv[0]) # Required Signals def deviceLost(devicepath): - print "Device lost: %s" % (devicepath) + print("Device lost: %s" % (devicepath)) def p2pStateChange(status): - print status + print(status) os._exit(0) class P2P_Stop_Find (threading.Thread): @@ -83,10 +83,10 @@ def __init__(self,interface_name,wpas_dbus_interface,timeout): try: self.path = self.wpas.GetInterface( self.interface_name) - except dbus.DBusException, exc: + except dbus.DBusException as exc: error = 'Error:\n Interface ' + self.interface_name \ + ' was not found' - print error + print(error) usage() os._exit(0) @@ -147,7 +147,7 @@ def run(self): # Interface name is required and was not given if (interface_name == None): - print "Error:\n interface_name is required" + print("Error:\n interface_name is required") usage() quit() @@ -157,7 +157,7 @@ def run(self): wpas_dbus_interface,timeout) except: - print "Error:\n Invalid wpas_dbus_interface" + print("Error:\n Invalid wpas_dbus_interface") usage() quit() @@ -170,5 +170,5 @@ def run(self): except: pass - print "p2p find stopped" + print("p2p find stopped") quit() diff --git a/wpa_supplicant/examples/wpas-dbus-new-getall.py b/wpa_supplicant/examples/wpas-dbus-new-getall.py index 03da187c8908..732f54d20f8b 100755 --- a/wpa_supplicant/examples/wpas-dbus-new-getall.py +++ b/wpa_supplicant/examples/wpas-dbus-new-getall.py @@ -11,8 +11,8 @@ def main(): "/fi/w1/wpa_supplicant1") props = wpas_obj.GetAll("fi.w1.wpa_supplicant1", dbus_interface=dbus.PROPERTIES_IFACE) - print "GetAll(fi.w1.wpa_supplicant1, /fi/w1/wpa_supplicant1):" - print props + print("GetAll(fi.w1.wpa_supplicant1, /fi/w1/wpa_supplicant1):") + print(props) if len(sys.argv) != 2: os._exit(1) @@ -24,15 +24,15 @@ def main(): if_obj = bus.get_object("fi.w1.wpa_supplicant1", path) props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface", dbus_interface=dbus.PROPERTIES_IFACE) - print - print "GetAll(fi.w1.wpa_supplicant1.Interface, %s):" % (path) - print props + print('') + print("GetAll(fi.w1.wpa_supplicant1.Interface, %s):" % (path)) + print(props) props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface.WPS", dbus_interface=dbus.PROPERTIES_IFACE) - print - print "GetAll(fi.w1.wpa_supplicant1.Interface.WPS, %s):" % (path) - print props + print('') + print("GetAll(fi.w1.wpa_supplicant1.Interface.WPS, %s):" % (path)) + print(props) res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'BSSs', dbus_interface=dbus.PROPERTIES_IFACE) @@ -40,9 +40,9 @@ def main(): bss_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0]) props = bss_obj.GetAll("fi.w1.wpa_supplicant1.BSS", dbus_interface=dbus.PROPERTIES_IFACE) - print - print "GetAll(fi.w1.wpa_supplicant1.BSS, %s):" % (res[0]) - print props + print('') + print("GetAll(fi.w1.wpa_supplicant1.BSS, %s):" % (res[0])) + print(props) res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'Networks', dbus_interface=dbus.PROPERTIES_IFACE) @@ -50,10 +50,9 @@ def main(): net_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0]) props = net_obj.GetAll("fi.w1.wpa_supplicant1.Network", dbus_interface=dbus.PROPERTIES_IFACE) - print - print "GetAll(fi.w1.wpa_supplicant1.Network, %s):" % (res[0]) - print props + print('') + print("GetAll(fi.w1.wpa_supplicant1.Network, %s):" % (res[0])) + print(props) if __name__ == "__main__": main() - diff --git a/wpa_supplicant/examples/wpas-dbus-new-signals.py b/wpa_supplicant/examples/wpas-dbus-new-signals.py index d90ef1878ca7..366a65546af6 100755 --- a/wpa_supplicant/examples/wpas-dbus-new-signals.py +++ b/wpa_supplicant/examples/wpas-dbus-new-signals.py @@ -32,17 +32,17 @@ def list_interfaces(wpas_obj): if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname', dbus_interface=dbus.PROPERTIES_IFACE) - print ifname + print(ifname) def interfaceAdded(interface, properties): - print "InterfaceAdded(%s): Ifname=%s" % (interface, properties['Ifname']) + print("InterfaceAdded(%s): Ifname=%s" % (interface, properties['Ifname'])) def interfaceRemoved(interface): - print "InterfaceRemoved(%s)" % (interface) + print("InterfaceRemoved(%s)" % (interface)) def propertiesChanged(properties): for i in properties: - print "PropertiesChanged: %s=%s" % (i, properties[i]) + print("PropertiesChanged: %s=%s" % (i, properties[i])) def showBss(bss): net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss) @@ -80,48 +80,48 @@ def showBss(bss): else: maxrate = 0 - print " %s :: ssid='%s' wpa=%s wpa2=%s signal=%d rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq) + print(" %s :: ssid='%s' wpa=%s wpa2=%s signal=%d rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq)) def scanDone(success): gobject.MainLoop().quit() - print "Scan done: success=%s" % success + print("Scan done: success=%s" % success) def scanDone2(success, path=None): - print "Scan done: success=%s [path=%s]" % (success, path) + print("Scan done: success=%s [path=%s]" % (success, path)) def bssAdded(bss, properties): - print "BSS added: %s" % (bss) + print("BSS added: %s" % (bss)) showBss(bss) def bssRemoved(bss): - print "BSS removed: %s" % (bss) + print("BSS removed: %s" % (bss)) def blobAdded(blob): - print "BlobAdded(%s)" % (blob) + print("BlobAdded(%s)" % (blob)) def blobRemoved(blob): - print "BlobRemoved(%s)" % (blob) + print("BlobRemoved(%s)" % (blob)) def networkAdded(network, properties): - print "NetworkAdded(%s)" % (network) + print("NetworkAdded(%s)" % (network)) def networkRemoved(network): - print "NetworkRemoved(%s)" % (network) + print("NetworkRemoved(%s)" % (network)) def networkSelected(network): - print "NetworkSelected(%s)" % (network) + print("NetworkSelected(%s)" % (network)) def propertiesChangedInterface(properties): for i in properties: - print "PropertiesChanged(interface): %s=%s" % (i, properties[i]) + print("PropertiesChanged(interface): %s=%s" % (i, properties[i])) def propertiesChangedBss(properties): for i in properties: - print "PropertiesChanged(BSS): %s=%s" % (i, properties[i]) + print("PropertiesChanged(BSS): %s=%s" % (i, properties[i])) def propertiesChangedNetwork(properties): for i in properties: - print "PropertiesChanged(Network): %s=%s" % (i, properties[i]) + print("PropertiesChanged(Network): %s=%s" % (i, properties[i])) def main(): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) diff --git a/wpa_supplicant/examples/wpas-dbus-new-wps.py b/wpa_supplicant/examples/wpas-dbus-new-wps.py index b8863858fe8c..7d87b1efd5dc 100755 --- a/wpa_supplicant/examples/wpas-dbus-new-wps.py +++ b/wpa_supplicant/examples/wpas-dbus-new-wps.py @@ -15,23 +15,23 @@ def propertiesChanged(properties): if properties.has_key("State"): - print "PropertiesChanged: State: %s" % (properties["State"]) + print("PropertiesChanged: State: %s" % (properties["State"])) def scanDone(success): - print "Scan done: success=%s" % success + print("Scan done: success=%s" % success) def bssAdded(bss, properties): - print "BSS added: %s" % (bss) + print("BSS added: %s" % (bss)) def bssRemoved(bss): - print "BSS removed: %s" % (bss) + print("BSS removed: %s" % (bss)) def wpsEvent(name, args): - print "WPS event: %s" % (name) - print args + print("WPS event: %s" % (name)) + print(args) def credentials(cred): - print "WPS credentials: %s" % (cred) + print("WPS credentials: %s" % (cred)) def main(): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) @@ -40,7 +40,7 @@ def main(): wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) if len(sys.argv) != 2: - print "Missing ifname argument" + print("Missing ifname argument") os._exit(1) wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) diff --git a/wpa_supplicant/examples/wpas-dbus-new.py b/wpa_supplicant/examples/wpas-dbus-new.py index 25072ce9a2df..6bf74ae44122 100755 --- a/wpa_supplicant/examples/wpas-dbus-new.py +++ b/wpa_supplicant/examples/wpas-dbus-new.py @@ -31,11 +31,11 @@ def list_interfaces(wpas_obj): if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname', dbus_interface=dbus.PROPERTIES_IFACE) - print ifname + print(ifname) def propertiesChanged(properties): if properties.has_key("State"): - print "PropertiesChanged: State: %s" % (properties["State"]) + print("PropertiesChanged: State: %s" % (properties["State"])) def showBss(bss): net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss) @@ -73,25 +73,25 @@ def showBss(bss): else: maxrate = 0 - print " %s :: ssid='%s' wpa=%s wpa2=%s signal=%d rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq) + print(" %s :: ssid='%s' wpa=%s wpa2=%s signal=%d rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq)) def scanDone(success): - print "Scan done: success=%s" % success + print("Scan done: success=%s" % success) res = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'BSSs', dbus_interface=dbus.PROPERTIES_IFACE) - print "Scanned wireless networks:" + print("Scanned wireless networks:") for opath in res: - print opath + print(opath) showBss(opath) def bssAdded(bss, properties): - print "BSS added: %s" % (bss) + print("BSS added: %s" % (bss)) showBss(bss) def bssRemoved(bss): - print "BSS removed: %s" % (bss) + print("BSS removed: %s" % (bss)) def main(): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) @@ -123,14 +123,14 @@ def main(): path = None try: path = wpas.GetInterface(ifname) - except dbus.DBusException, exc: + except dbus.DBusException as exc: if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceUnknown:"): raise exc try: path = wpas.CreateInterface({'Ifname': ifname, 'Driver': 'test'}) time.sleep(1) - except dbus.DBusException, exc: + except dbus.DBusException as exc: if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceExists:"): raise exc diff --git a/wpa_supplicant/examples/wpas-test.py b/wpa_supplicant/examples/wpas-test.py deleted file mode 100755 index fd7f73d428b8..000000000000 --- a/wpa_supplicant/examples/wpas-test.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/python - -import dbus -import sys, os -import time - -WPAS_DBUS_SERVICE = "fi.epitest.hostap.WPASupplicant" -WPAS_DBUS_INTERFACE = "fi.epitest.hostap.WPASupplicant" -WPAS_DBUS_OPATH = "/fi/epitest/hostap/WPASupplicant" - -WPAS_DBUS_INTERFACES_INTERFACE = "fi.epitest.hostap.WPASupplicant.Interface" -WPAS_DBUS_INTERFACES_OPATH = "/fi/epitest/hostap/WPASupplicant/Interfaces" -WPAS_DBUS_BSSID_INTERFACE = "fi.epitest.hostap.WPASupplicant.BSSID" - -def byte_array_to_string(s): - import urllib - r = "" - for c in s: - if c >= 32 and c < 127: - r += "%c" % c - else: - r += urllib.quote(chr(c)) - return r - -def main(): - if len(sys.argv) != 2: - print "Usage: wpas-test.py " - os._exit(1) - - ifname = sys.argv[1] - - bus = dbus.SystemBus() - wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) - wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) - - # See if wpa_supplicant already knows about this interface - path = None - try: - path = wpas.getInterface(ifname) - except dbus.dbus_bindings.DBusException, exc: - if str(exc) != "wpa_supplicant knows nothing about this interface.": - raise exc - try: - path = wpas.addInterface(ifname, {'driver': dbus.Variant('wext')}) - except dbus.dbus_bindings.DBusException, exc: - if str(exc) != "wpa_supplicant already controls this interface.": - raise exc - - if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) - iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) - iface.scan() - # Should really wait for the "scanResults" signal instead of sleeping - time.sleep(5) - res = iface.scanResults() - - print "Scanned wireless networks:" - for opath in res: - net_obj = bus.get_object(WPAS_DBUS_SERVICE, opath) - net = dbus.Interface(net_obj, WPAS_DBUS_BSSID_INTERFACE) - props = net.properties() - - # Convert the byte-array for SSID and BSSID to printable strings - bssid = "" - for item in props["bssid"]: - bssid = bssid + ":%02x" % item - bssid = bssid[1:] - ssid = byte_array_to_string(props["ssid"]) - wpa = "no" - if props.has_key("wpaie"): - wpa = "yes" - wpa2 = "no" - if props.has_key("rsnie"): - wpa2 = "yes" - freq = 0 - if props.has_key("frequency"): - freq = props["frequency"] - caps = props["capabilities"] - qual = props["quality"] - level = props["level"] - noise = props["noise"] - maxrate = props["maxrate"] / 1000000 - - print " %s :: ssid='%s' wpa=%s wpa2=%s quality=%d%% rate=%d freq=%d" % (bssid, ssid, wpa, wpa2, qual, maxrate, freq) - - wpas.removeInterface(dbus.ObjectPath(path)) - # Should fail here with unknown interface error - iface.scan() - -if __name__ == "__main__": - main() - diff --git a/wpa_supplicant/examples/wps-nfc.py b/wpa_supplicant/examples/wps-nfc.py index 7459eb9ae574..bb458fb37a84 100755 --- a/wpa_supplicant/examples/wps-nfc.py +++ b/wpa_supplicant/examples/wps-nfc.py @@ -30,7 +30,7 @@ success_file = None def summary(txt): - print txt + print(txt) if summary_file: with open(summary_file, 'a') as f: f.write(txt + "\n") @@ -46,19 +46,19 @@ def wpas_connect(): if os.path.isdir(wpas_ctrl): try: ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] - except OSError, error: - print "Could not find wpa_supplicant: ", error + except OSError as error: + print("Could not find wpa_supplicant: ", error) return None if len(ifaces) < 1: - print "No wpa_supplicant control interface found" + print("No wpa_supplicant control interface found") return None for ctrl in ifaces: try: wpas = wpaspy.Ctrl(ctrl) return wpas - except Exception, e: + except Exception as e: pass return None @@ -163,22 +163,22 @@ def process_request(self, request): self.ho_server_processing = True summary("HandoverServer - request received") try: - print "Parsed handover request: " + request.pretty() - except Exception, e: - print e + print("Parsed handover request: " + request.pretty()) + except Exception as e: + print(e) sel = nfc.ndef.HandoverSelectMessage(version="1.2") for carrier in request.carriers: - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.wsc": summary("WPS carrier type match - add WPS carrier record") data = wpas_get_handover_sel(self.uuid) if data is None: summary("Could not get handover select carrier record from wpa_supplicant") continue - print "Handover select carrier record from wpa_supplicant:" - print data.encode("hex") + print("Handover select carrier record from wpa_supplicant:") + print(data.encode("hex")) self.sent_carrier = data if "OK" in wpas_report_handover(carrier.record, self.sent_carrier, "RESP"): success_report("Handover reported successfully (responder)") @@ -188,12 +188,12 @@ def process_request(self, request): message = nfc.ndef.Message(data); sel.add_carrier(message[0], "active", message[1:]) - print "Handover select:" + print("Handover select:") try: - print sel.pretty() - except Exception, e: - print e - print str(sel).encode("hex") + print(sel.pretty()) + except Exception as e: + print(e) + print(str(sel).encode("hex")) summary("Sending handover select") self.success = True @@ -207,19 +207,19 @@ def wps_handover_init(llc): if (data == None): summary("Could not get handover request carrier record from wpa_supplicant") return - print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") + print("Handover request carrier record from wpa_supplicant: " + data.encode("hex")) message = nfc.ndef.HandoverRequestMessage(version="1.2") message.nonce = random.randint(0, 0xffff) datamsg = nfc.ndef.Message(data) message.add_carrier(datamsg[0], "active", datamsg[1:]) - print "Handover request:" + print("Handover request:") try: - print message.pretty() - except Exception, e: - print e - print str(message).encode("hex") + print(message.pretty()) + except Exception as e: + print(e) + print(str(message).encode("hex")) client = nfc.handover.HandoverClient(llc) try: @@ -230,7 +230,7 @@ def wps_handover_init(llc): summary("Handover connection refused") client.close() return - except Exception, e: + except Exception as e: summary("Other exception: " + str(e)) client.close() return @@ -253,23 +253,23 @@ def wps_handover_init(llc): client.close() return - print "Received message" + print("Received message") try: - print message.pretty() - except Exception, e: - print e - print str(message).encode("hex") + print(message.pretty()) + except Exception as e: + print(e) + print(str(message).encode("hex")) message = nfc.ndef.HandoverSelectMessage(message) summary("Handover select received") try: - print message.pretty() - except Exception, e: - print e + print(message.pretty()) + except Exception as e: + print(e) for carrier in message.carriers: - print "Remote carrier type: " + carrier.type + print("Remote carrier type: " + carrier.type) if carrier.type == "application/vnd.wfa.wsc": - print "WPS carrier type match - send to wpa_supplicant" + print("WPS carrier type match - send to wpa_supplicant") if "OK" in wpas_report_handover(data, carrier.record, "INIT"): success_report("Handover reported successfully (initiator)") else: @@ -278,9 +278,9 @@ def wps_handover_init(llc): #wifi = nfc.ndef.WifiConfigRecord(carrier.record) #print wifi.pretty() - print "Remove peer" + print("Remove peer") client.close() - print "Done with handover" + print("Done with handover") global only_one if only_one: global continue_loop @@ -288,7 +288,7 @@ def wps_handover_init(llc): global no_wait if no_wait: - print "Trying to exit.." + print("Trying to exit..") global terminate_now terminate_now = True @@ -296,7 +296,7 @@ def wps_tag_read(tag, wait_remove=True): success = False if len(tag.ndef.message): for record in tag.ndef.message: - print "record type " + record.type + print("record type " + record.type) if record.type == "application/vnd.wfa.wsc": summary("WPS tag - send to wpa_supplicant") success = wpas_tag_read(tag.ndef.message) @@ -308,7 +308,7 @@ def wps_tag_read(tag, wait_remove=True): success_report("Tag read succeeded") if wait_remove: - print "Remove tag" + print("Remove tag") while tag.is_present: time.sleep(0.1) @@ -320,7 +320,7 @@ def rdwr_connected_write(tag): global write_data tag.ndef.message = str(write_data) success_report("Tag write succeeded") - print "Done - remove tag" + print("Done - remove tag") global only_one if only_one: global continue_loop @@ -330,41 +330,41 @@ def rdwr_connected_write(tag): time.sleep(0.1) def wps_write_config_tag(clf, id=None, wait_remove=True): - print "Write WPS config token" + print("Write WPS config token") global write_data, write_wait_remove write_wait_remove = wait_remove write_data = wpas_get_config_token(id) if write_data == None: - print "Could not get WPS config token from wpa_supplicant" + print("Could not get WPS config token from wpa_supplicant") sys.exit(1) return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) def wps_write_er_config_tag(clf, uuid, wait_remove=True): - print "Write WPS ER config token" + print("Write WPS ER config token") global write_data, write_wait_remove write_wait_remove = wait_remove write_data = wpas_get_er_config_token(uuid) if write_data == None: - print "Could not get WPS config token from wpa_supplicant" + print("Could not get WPS config token from wpa_supplicant") return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) def wps_write_password_tag(clf, wait_remove=True): - print "Write WPS password token" + print("Write WPS password token") global write_data, write_wait_remove write_wait_remove = wait_remove write_data = wpas_get_password_token() if write_data == None: - print "Could not get WPS password token from wpa_supplicant" + print("Could not get WPS password token from wpa_supplicant") return - print "Touch an NFC tag" + print("Touch an NFC tag") clf.connect(rdwr={'on-connect': rdwr_connected_write}) @@ -373,11 +373,11 @@ def rdwr_connected(tag): summary("Tag connected: " + str(tag)) if tag.ndef: - print "NDEF tag: " + tag.type + print("NDEF tag: " + tag.type) try: - print tag.ndef.message.pretty() - except Exception, e: - print e + print(tag.ndef.message.pretty()) + except Exception as e: + print(e) success = wps_tag_read(tag, not only_one) if only_one and success: global continue_loop @@ -393,7 +393,7 @@ def llcp_worker(llc): global arg_uuid if arg_uuid is None: wps_handover_init(llc) - print "Exiting llcp_worker thread" + print("Exiting llcp_worker thread") return global srv @@ -405,19 +405,19 @@ def llcp_worker(llc): def llcp_startup(clf, llc): global arg_uuid if arg_uuid: - print "Start LLCP server" + print("Start LLCP server") global srv srv = HandoverServer(llc) if arg_uuid is "ap": - print "Trying to handle WPS handover" + print("Trying to handle WPS handover") srv.uuid = None else: - print "Trying to handle WPS handover with AP " + arg_uuid + print("Trying to handle WPS handover with AP " + arg_uuid) srv.uuid = arg_uuid return llc def llcp_connected(llc): - print "P2P LLCP connected" + print("P2P LLCP connected") global wait_connection wait_connection = False global arg_uuid @@ -426,7 +426,7 @@ def llcp_connected(llc): srv.start() else: threading.Thread(target=llcp_worker, args=(llc,)).start() - print "llcp_connected returning" + print("llcp_connected returning") return True @@ -482,7 +482,7 @@ def main(): try: if not clf.open("usb"): - print "Could not open connection with an NFC device" + print("Could not open connection with an NFC device") raise SystemExit if args.command == "write-config": @@ -499,7 +499,7 @@ def main(): global continue_loop while continue_loop: - print "Waiting for a tag or peer to be touched" + print("Waiting for a tag or peer to be touched") wait_connection = True try: if not clf.connect(rdwr={'on-connect': rdwr_connected}, @@ -507,8 +507,8 @@ def main(): 'on-connect': llcp_connected}, terminate=terminate_loop): break - except Exception, e: - print "clf.connect failed" + except Exception as e: + print("clf.connect failed") global srv if only_one and srv and srv.success: diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index f4f60c58bee5..8e977a3eccb3 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -272,7 +272,7 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, } -static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) +int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) { if (wpa_s->current_ssid == NULL || wpa_s->wpa_state < WPA_4WAY_HANDSHAKE || diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h index 982c0f7ce60e..d2b455442f0a 100644 --- a/wpa_supplicant/gas_query.h +++ b/wpa_supplicant/gas_query.h @@ -19,6 +19,7 @@ void gas_query_deinit(struct gas_query *gas); int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, const u8 *bssid, u8 categ, const u8 *data, size_t len, int freq); +int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr); /** * enum gas_query_result - GAS query result diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index f4187900ed42..cb236df18d86 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -95,8 +95,7 @@ void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) return; } - /* Check if Proxy ARP is enabled (2nd byte in the IE) */ - if (ext_capa[3] & BIT(4)) + if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_PROXY_ARP)) filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | WPA_DATA_FRAME_FILTER_FLAG_NA; @@ -104,15 +103,22 @@ void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) } -void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id) +void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, int ap_release) { + int release; u8 conf; + release = (HS20_VERSION >> 4) + 1; + if (ap_release > 0 && release > ap_release) + release = ap_release; + if (release < 2) + pps_mo_id = -1; + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE); - conf = HS20_VERSION; + conf = (release - 1) << 4; if (pps_mo_id >= 0) conf |= HS20_PPS_MO_ID_PRESENT; wpabuf_put_u8(buf, conf); @@ -137,6 +143,21 @@ void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, } +int get_hs20_version(struct wpa_bss *bss) +{ + const u8 *ie; + + if (!bss) + return 0; + + ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); + if (!ie || ie[1] < 5) + return 0; + + return ((ie[6] >> 4) & 0x0f) + 1; +} + + int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss) { @@ -410,7 +431,7 @@ static void hs20_set_osu_access_permission(const char *osu_dir, return; } - if (chown(fname, statbuf.st_uid, statbuf.st_gid) < 0) { + if (lchown(fname, statbuf.st_uid, statbuf.st_gid) < 0) { wpa_printf(MSG_WARNING, "Cannot change the ownership for %s", fname); } diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h index 66fc540be3e4..e43414bc65c5 100644 --- a/wpa_supplicant/hs20_supplicant.h +++ b/wpa_supplicant/hs20_supplicant.h @@ -9,7 +9,8 @@ #define HS20_SUPPLICANT_H void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s); -void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id); +void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id, + int ap_release); void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, const struct wpa_ssid *ssid); @@ -20,6 +21,7 @@ void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, const u8 *data, size_t slen, u8 dialog_token); +int get_hs20_version(struct wpa_bss *bss); int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 00919d14a55e..e96ea65a7887 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -260,12 +260,14 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level, static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk, - size_t *psk_len) + size_t *psk_len, int *vlan_id) { struct ibss_rsn *ibss_rsn = ctx; if (psk_len) *psk_len = PMK_LEN; + if (vlan_id) + *vlan_id = 0; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", __func__, MAC2STR(addr), prev_psk); if (prev_psk) @@ -457,7 +459,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, } /* TODO: get peer RSN IE with Probe Request */ - if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, + if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, 0, (u8 *) "\x30\x14\x01\x00" "\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x04" diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 60c8be9a6c6a..f94cd1614c60 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -958,6 +958,7 @@ static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP"; if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0 || wpa_config_set(ssid, "proto", "RSN", 0) < 0 || + wpa_config_set(ssid, "ieee80211w", "1", 0) < 0 || wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) return -1; return 0; @@ -2625,7 +2626,6 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; int found = 0; - const u8 *ie; wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - " "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d", @@ -2648,8 +2648,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!(bss->caps & IEEE80211_CAP_ESS)) continue; - ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); - if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80)) + if (!wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_INTERWORKING)) continue; /* AP does not support Interworking */ if (disallowed_bssid(wpa_s, bss->bssid) || disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) @@ -2982,7 +2981,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, MAC2STR(sa)); anqp_add_extra(wpa_s, anqp, info_id, pos, slen); - if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { + if (!pmf_in_use(wpa_s, sa)) { wpa_printf(MSG_DEBUG, "ANQP: Ignore Venue URL since PMF was not enabled"); break; diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index e08c2fd266f1..51a8a0298a9b 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -28,9 +28,9 @@ static void usage(void) "s" #endif /* CONFIG_DEBUG_SYSLOG */ "t" -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW "u" -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ "vW] [-P] " "[-g] \\\n" " [-G] \\\n" @@ -98,9 +98,9 @@ static void usage(void) " -T = record to Linux tracing in addition to logging\n" " (records all messages regardless of debug verbosity)\n" #endif /* CONFIG_DEBUG_LINUX_TRACING */ -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW " -u = enable DBus control interface\n" -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ " -v = show version\n" " -W = wait for a control interface monitor before starting\n"); @@ -295,11 +295,11 @@ int main(int argc, char *argv[]) case 't': params.wpa_debug_timestamp++; break; -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW case 'u': params.dbus_ctrl_interface = 1; break; -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ case 'v': printf("%s\n", wpa_supplicant_version); exitcode = 0; diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c index 5adf61e58bd0..43b1fa7beb38 100644 --- a/wpa_supplicant/mbo.c +++ b/wpa_supplicant/mbo.c @@ -51,6 +51,19 @@ const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr) } +const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len, + enum mbo_attr_id attr) +{ + const u8 *mbo_ie; + + mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE); + if (!mbo_ie) + return NULL; + + return mbo_attr_from_mbo_ie(mbo_ie, attr); +} + + const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) { const u8 *mbo, *end; @@ -85,6 +98,13 @@ static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, } +static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size) +{ + wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); + wpabuf_put_u8(mbo, size); /* Length */ +} + + static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, struct wpabuf *mbo, u8 start, u8 end) { @@ -93,9 +113,7 @@ static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, if (size + 2 > wpabuf_tailroom(mbo)) return; - wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); - wpabuf_put_u8(mbo, size); /* Length */ - + wpas_mbo_non_pref_chan_attr_hdr(mbo, size); wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); } @@ -132,6 +150,8 @@ static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { if (subelement) wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); + else + wpas_mbo_non_pref_chan_attr_hdr(mbo, 0); return; } start_pref = &wpa_s->non_pref_chan[0]; @@ -255,6 +275,7 @@ static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), wpabuf_len(buf)); + wpas_update_mbo_connect_params(wpa_s); wpabuf_free(buf); } @@ -280,10 +301,10 @@ static int wpa_non_pref_chan_cmp(const void *_a, const void *_b) const struct wpa_mbo_non_pref_channel *a = _a, *b = _b; if (a->oper_class != b->oper_class) - return a->oper_class - b->oper_class; + return (int) a->oper_class - (int) b->oper_class; if (a->reason != b->reason) - return a->reason - b->reason; - return a->preference - b->preference; + return (int) a->reason - (int) b->reason; + return (int) a->preference - (int) b->preference; } @@ -501,7 +522,7 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, if (disallowed_sec && wpa_s->current_bss) wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, - disallowed_sec); + disallowed_sec, 0); return; fail: @@ -545,6 +566,7 @@ void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7); wpa_supplicant_set_default_scan_ies(wpa_s); + wpas_update_mbo_connect_params(wpa_s); } diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index 38b9fb320ca9..92600211ac2d 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -34,6 +34,8 @@ static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s) wpa_s->current_ssid = NULL; os_free(wpa_s->mesh_rsn); wpa_s->mesh_rsn = NULL; + os_free(wpa_s->mesh_params); + wpa_s->mesh_params = NULL; /* TODO: leave mesh (stop beacon). This will happen on link down * anyway, so it's not urgent */ } @@ -93,6 +95,9 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, conf->ieee80211w = NO_MGMT_FRAME_PROTECTION; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + conf->ocv = ssid->ocv; +#endif /* CONFIG_OCV */ cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0); if (cipher < 0 || cipher == WPA_CIPHER_TKIP) { @@ -147,6 +152,93 @@ static void wpas_mesh_copy_groups(struct hostapd_data *bss, } +static int wpas_mesh_init_rsn(struct wpa_supplicant *wpa_s) +{ + struct hostapd_iface *ifmsh = wpa_s->ifmsh; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_data *bss = ifmsh->bss[0]; + static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; + const char *password; + size_t len; + + password = ssid->sae_password; + if (!password) + password = ssid->passphrase; + if (!password) { + wpa_printf(MSG_ERROR, + "mesh: Passphrase for SAE not configured"); + return -1; + } + + bss->conf->wpa = ssid->proto; + bss->conf->wpa_key_mgmt = ssid->key_mgmt; + + if (wpa_s->conf->sae_groups && wpa_s->conf->sae_groups[0] > 0) { + wpas_mesh_copy_groups(bss, wpa_s); + } else { + bss->conf->sae_groups = os_memdup(default_groups, + sizeof(default_groups)); + if (!bss->conf->sae_groups) + return -1; + } + + len = os_strlen(password); + bss->conf->ssid.wpa_passphrase = dup_binstr(password, len); + + wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, ifmsh->mconf); + return !wpa_s->mesh_rsn ? -1 : 0; +} + + +static int wpas_mesh_complete(struct wpa_supplicant *wpa_s) +{ + struct hostapd_iface *ifmsh = wpa_s->ifmsh; + struct wpa_driver_mesh_join_params *params = wpa_s->mesh_params; + struct wpa_ssid *ssid = wpa_s->current_ssid; + int ret; + + if (!params || !ssid || !ifmsh) { + wpa_printf(MSG_ERROR, "mesh: %s called without active mesh", + __func__); + return -1; + } + + if (ifmsh->mconf->security != MESH_CONF_SEC_NONE && + wpas_mesh_init_rsn(wpa_s)) { + wpa_printf(MSG_ERROR, + "mesh: RSN initialization failed - deinit mesh"); + wpa_supplicant_mesh_deinit(wpa_s); + return -1; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher; + wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher; + wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; + } + + params->ies = ifmsh->mconf->rsn_ie; + params->ie_len = ifmsh->mconf->rsn_ie_len; + params->basic_rates = ifmsh->basic_rates; + params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; + params->conf.ht_opmode = ifmsh->bss[0]->iface->ht_op_mode; + + wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ret = wpa_drv_join_mesh(wpa_s, params); + if (ret) + wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret); + + /* hostapd sets the interface down until we associate */ + wpa_drv_set_operstate(wpa_s, 1); + + if (!ret) + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + + return ret; +} + + static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_freq_params *freq) @@ -156,9 +248,6 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, struct hostapd_config *conf; struct mesh_conf *mconf; int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 }; - static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; - const char *password; - size_t len; int rate_len; int frequency; @@ -208,6 +297,16 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, bss->conf->start_disabled = 1; bss->conf->mesh = MESH_ENABLED; bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity; + + if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes, + wpa_s->hw.num_modes) && wpa_s->conf->country[0]) { + conf->ieee80211h = 1; + conf->ieee80211d = 1; + conf->country[0] = wpa_s->conf->country[0]; + conf->country[1] = wpa_s->conf->country[1]; + conf->country[2] = ' '; + } + bss->iconf = conf; ifmsh->conf = conf; @@ -231,7 +330,8 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, if (ssid->ht40) conf->secondary_channel = ssid->ht40; if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) { - conf->vht_oper_chwidth = ssid->max_oper_chwidth; + if (ssid->max_oper_chwidth != DEFAULT_MAX_OPER_CHWIDTH) + conf->vht_oper_chwidth = ssid->max_oper_chwidth; switch (conf->vht_oper_chwidth) { case VHT_CHANWIDTH_80MHZ: case VHT_CHANWIDTH_80P80MHZ: @@ -281,48 +381,15 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, conf->basic_rates[rate_len] = -1; } - if (hostapd_setup_interface(ifmsh)) { - wpa_printf(MSG_ERROR, - "Failed to initialize hostapd interface for mesh"); - return -1; - } - if (wpa_drv_init_mesh(wpa_s)) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver"); return -1; } - if (mconf->security != MESH_CONF_SEC_NONE) { - password = ssid->sae_password; - if (!password) - password = ssid->passphrase; - if (!password) { - wpa_printf(MSG_ERROR, - "mesh: Passphrase for SAE not configured"); - goto out_free; - } - - bss->conf->wpa = ssid->proto; - bss->conf->wpa_key_mgmt = ssid->key_mgmt; - - if (wpa_s->conf->sae_groups && - wpa_s->conf->sae_groups[0] > 0) { - wpas_mesh_copy_groups(bss, wpa_s); - } else { - bss->conf->sae_groups = - os_memdup(default_groups, - sizeof(default_groups)); - if (!bss->conf->sae_groups) - goto out_free; - } - - len = os_strlen(password); - bss->conf->ssid.wpa_passphrase = - dup_binstr(password, len); - - wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf); - if (!wpa_s->mesh_rsn) - goto out_free; + if (hostapd_setup_interface(ifmsh)) { + wpa_printf(MSG_ERROR, + "Failed to initialize hostapd interface for mesh"); + return -1; } wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); @@ -367,11 +434,13 @@ void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - struct wpa_driver_mesh_join_params params; + struct wpa_driver_mesh_join_params *params = os_zalloc(sizeof(*params)); int ret = 0; - if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) { + if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency || + !params) { ret = -ENOENT; + os_free(params); goto out; } @@ -381,22 +450,23 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, wpa_s->group_cipher = WPA_CIPHER_NONE; wpa_s->mgmt_group_cipher = 0; - os_memset(¶ms, 0, sizeof(params)); - params.meshid = ssid->ssid; - params.meshid_len = ssid->ssid_len; - ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); - wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled; - wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled; - if (params.freq.ht_enabled && params.freq.sec_channel_offset) - ssid->ht40 = params.freq.sec_channel_offset; + params->meshid = ssid->ssid; + params->meshid_len = ssid->ssid_len; + ibss_mesh_setup_freq(wpa_s, ssid, ¶ms->freq); + wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled; + wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled; + if (params->freq.ht_enabled && params->freq.sec_channel_offset) + ssid->ht40 = params->freq.sec_channel_offset; + if (wpa_s->mesh_vht_enabled) { ssid->vht = 1; - switch (params.freq.bandwidth) { + ssid->vht_center_freq1 = params->freq.center_freq1; + switch (params->freq.bandwidth) { case 80: - if (params.freq.center_freq2) { + if (params->freq.center_freq2) { ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; ssid->vht_center_freq2 = - params.freq.center_freq2; + params->freq.center_freq2; } else { ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ; } @@ -410,67 +480,44 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, } } if (ssid->beacon_int > 0) - params.beacon_int = ssid->beacon_int; + params->beacon_int = ssid->beacon_int; else if (wpa_s->conf->beacon_int > 0) - params.beacon_int = wpa_s->conf->beacon_int; + params->beacon_int = wpa_s->conf->beacon_int; if (ssid->dtim_period > 0) - params.dtim_period = ssid->dtim_period; + params->dtim_period = ssid->dtim_period; else if (wpa_s->conf->dtim_period > 0) - params.dtim_period = wpa_s->conf->dtim_period; - params.conf.max_peer_links = wpa_s->conf->max_peer_links; + params->dtim_period = wpa_s->conf->dtim_period; + params->conf.max_peer_links = wpa_s->conf->max_peer_links; if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) { - params.conf.rssi_threshold = ssid->mesh_rssi_threshold; - params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD; + params->conf.rssi_threshold = ssid->mesh_rssi_threshold; + params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD; } if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { - params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; - params.flags |= WPA_DRIVER_MESH_FLAG_AMPE; + params->flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; + params->flags |= WPA_DRIVER_MESH_FLAG_AMPE; wpa_s->conf->user_mpm = 1; } if (wpa_s->conf->user_mpm) { - params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; - params.conf.auto_plinks = 0; + params->flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; + params->conf.auto_plinks = 0; } else { - params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; - params.conf.auto_plinks = 1; + params->flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; + params->conf.auto_plinks = 1; } - params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; + params->conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; - if (wpa_supplicant_mesh_init(wpa_s, ssid, ¶ms.freq)) { + os_free(wpa_s->mesh_params); + wpa_s->mesh_params = params; + if (wpa_supplicant_mesh_init(wpa_s, ssid, ¶ms->freq)) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh"); wpa_drv_leave_mesh(wpa_s); ret = -1; goto out; } - if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { - wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher; - wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher; - wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; - } - - if (wpa_s->ifmsh) { - params.ies = wpa_s->ifmsh->mconf->rsn_ie; - params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len; - params.basic_rates = wpa_s->ifmsh->basic_rates; - params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; - params.conf.ht_opmode = wpa_s->ifmsh->bss[0]->iface->ht_op_mode; - } - - wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", - wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - ret = wpa_drv_join_mesh(wpa_s, ¶ms); - if (ret) - wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret); - - /* hostapd sets the interface down until we associate */ - wpa_drv_set_operstate(wpa_s, 1); - - if (!ret) - wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); - + ret = wpas_mesh_complete(wpa_s); out: return ret; } diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index eafb0af7b82a..9d6ab8da1ebe 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" +#include "common/ocv.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ieee802_11.h" @@ -188,7 +189,7 @@ static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, do { if (os_get_random((u8 *) &llid, sizeof(llid)) < 0) - continue; + llid = 0; /* continue */ } while (!llid || llid_in_use(wpa_s, llid)); sta->my_lid = llid; @@ -246,6 +247,11 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211AC */ if (type != PLINK_CLOSE) buf_len += conf->rsn_ie_len; /* RSN IE */ +#ifdef CONFIG_OCV + /* OCI is included even when the other STA doesn't support OCV */ + if (type != PLINK_CLOSE && conf->ocv) + buf_len += OCV_OCI_EXTENDED_LEN; +#endif /* CONFIG_OCV */ buf = wpabuf_alloc(buf_len); if (!buf) @@ -357,6 +363,22 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_OCV + if (type != PLINK_CLOSE && conf->ocv) { + struct wpa_channel_info ci; + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Mesh MPM: Failed to get channel info for OCI element"); + goto fail; + } + + pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN); + if (ocv_insert_extended_oci(&ci, pos) < 0) + goto fail; + } +#endif /* CONFIG_OCV */ + if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: failed to add AMPE and MIC IE"); @@ -699,6 +721,7 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211AC copy_sta_vht_capab(data, sta, elems->vht_capabilities); + copy_sta_vht_oper(data, sta, elems->vht_operation); set_sta_vht_opmode(data, sta, elems->vht_opmode_notif); #endif /* CONFIG_IEEE80211AC */ @@ -1196,6 +1219,56 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, } return; } + +#ifdef CONFIG_OCV + if (action_field == PLINK_OPEN && elems.rsn_ie) { + struct wpa_state_machine *sm = sta->wpa_sm; + struct wpa_ie_data data; + + res = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, + elems.rsn_ie_len + 2, + &data); + if (res) { + wpa_printf(MSG_DEBUG, + "Failed to parse RSN IE (res=%d)", + res); + wpa_hexdump(MSG_DEBUG, "RSN IE", elems.rsn_ie, + elems.rsn_ie_len); + return; + } + + wpa_auth_set_ocv(sm, mconf->ocv && + (data.capabilities & + WPA_CAPABILITY_OCVC)); + } + + if (action_field != PLINK_CLOSE && + wpa_auth_uses_ocv(sta->wpa_sm)) { + struct wpa_channel_info ci; + int tx_chanwidth; + int tx_seg1_idx; + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_printf(MSG_WARNING, + "MPM: Failed to get channel info to validate received OCI in MPM Confirm"); + return; + } + + if (get_tx_parameters( + sta, channel_width_to_int(ci.chanwidth), + ci.seg1_idx, &tx_chanwidth, + &tx_seg1_idx) < 0) + return; + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + tx_chanwidth, tx_seg1_idx) != + 0) { + wpa_printf(MSG_WARNING, "MPM: %s", + ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ } if (sta->plink_state == PLINK_BLOCKED) { diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index e74cb16b0725..4b8d6c469173 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -76,7 +76,7 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level, static const u8 *auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, const u8 *prev_psk, - size_t *psk_len) + size_t *psk_len, int *vlan_id) { struct mesh_rsn *mesh_rsn = ctx; struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; @@ -84,6 +84,8 @@ static const u8 *auth_get_psk(void *ctx, const u8 *addr, if (psk_len) *psk_len = PMK_LEN; + if (vlan_id) + *vlan_id = 0; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", __func__, MAC2STR(addr), prev_psk); @@ -140,7 +142,7 @@ static int auth_start_ampe(void *ctx, const u8 *addr) static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, - enum mfp_options ieee80211w) + enum mfp_options ieee80211w, int ocv) { struct wpa_auth_config conf; static const struct wpa_auth_callbacks cb = { @@ -168,6 +170,9 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, if (ieee80211w != NO_MGMT_FRAME_PROTECTION) conf.group_mgmt_cipher = rsn->mgmt_group_cipher; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + conf.ocv = ocv; +#endif /* CONFIG_OCV */ rsn->auth = wpa_init(addr, &conf, &cb, rsn); if (rsn->auth == NULL) { @@ -240,7 +245,7 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher; if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr, - conf->ieee80211w) < 0) { + conf->ieee80211w, conf->ocv) < 0) { mesh_rsn_deinit(mesh_rsn); os_free(mesh_rsn); return NULL; @@ -638,7 +643,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, size_t crypt_len; const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, - (elems->mic - 2) - cat }; + elems->mic ? (elems->mic - 2) - cat : 0 }; size_t key_len; if (!sta->sae) { @@ -652,7 +657,9 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, mesh_rsn_auth_sae_sta(wpa_s, sta); } - if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { + if (chosen_pmk && + (!sta->sae || + os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN) != 0)) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); return -1; diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 83df04f394c7..bedb74b34440 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -15,7 +15,6 @@ #include "wps_supplicant.h" #include "binder/binder.h" #include "dbus/dbus_common.h" -#include "dbus/dbus_old.h" #include "dbus/dbus_new.h" #include "rsn_supp/wpa.h" #include "fst/fst.h" @@ -27,13 +26,13 @@ int wpas_notify_supplicant_initialized(struct wpa_global *global) { -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW if (global->params.dbus_ctrl_interface) { global->dbus = wpas_dbus_init(global); if (global->dbus == NULL) return -1; } -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #ifdef CONFIG_BINDER global->binder = wpas_binder_init(global); @@ -47,10 +46,10 @@ int wpas_notify_supplicant_initialized(struct wpa_global *global) void wpas_notify_supplicant_deinitialized(struct wpa_global *global) { -#ifdef CONFIG_DBUS +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW if (global->dbus) wpas_dbus_deinit(global->dbus); -#endif /* CONFIG_DBUS */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #ifdef CONFIG_BINDER if (global->binder) @@ -64,9 +63,6 @@ int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return 0; - if (wpas_dbus_register_iface(wpa_s)) - return -1; - if (wpas_dbus_register_interface(wpa_s)) return -1; @@ -79,9 +75,6 @@ void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return; - /* unregister interface in old DBus ctrl iface */ - wpas_dbus_unregister_iface(wpa_s); - /* unregister interface in new DBus ctrl iface */ wpas_dbus_unregister_interface(wpa_s); } @@ -94,10 +87,6 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) return; - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_state_change(wpa_s, new_state, - old_state); - /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE); @@ -140,6 +129,15 @@ void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) } +void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AUTH_STATUS_CODE); +} + + void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_mgmt) @@ -149,6 +147,42 @@ void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s) } +void wpas_notify_roam_time(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ROAM_TIME); +} + + +void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ROAM_COMPLETE); +} + + +void wpas_notify_session_length(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SESSION_LENGTH); +} + + +void wpas_notify_bss_tm_status(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSS_TM_STATUS); +} + + void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_mgmt) @@ -222,9 +256,6 @@ void wpas_notify_scanning(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return; - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_scanning(wpa_s); - /* notify the new DBus API */ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING); } @@ -244,9 +275,6 @@ void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) if (wpa_s->p2p_mgmt) return; - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_scan_results(wpa_s); - wpas_wps_notify_scan_results(wpa_s); } @@ -258,8 +286,6 @@ void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, return; #ifdef CONFIG_WPS - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred); /* notify the new DBus API */ wpas_dbus_signal_wps_cred(wpa_s, cred); #endif /* CONFIG_WPS */ @@ -720,6 +746,9 @@ static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr); #endif /* CONFIG_P2P */ + /* Register the station */ + wpas_dbus_register_sta(wpa_s, sta); + /* Notify listeners a new station has been authorized */ wpas_dbus_signal_sta_authorized(wpa_s, sta); } @@ -740,6 +769,9 @@ static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s, /* Notify listeners a station has been deauthorized */ wpas_dbus_signal_sta_deauthorized(wpa_s, sta); + + /* Unregister the station */ + wpas_dbus_unregister_sta(wpa_s, sta); } @@ -787,9 +819,6 @@ void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, "depth=%d %s", depth, altsubject[i]); } - /* notify the old DBus API */ - wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject, - cert_hash, cert); /* notify the new DBus API */ wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject, num_altsubject, cert_hash, cert); diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index 3ca933c7621a..65f513ea9770 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -23,7 +23,12 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state); void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s); +void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s); void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s); +void wpas_notify_roam_time(struct wpa_supplicant *wpa_s); +void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s); +void wpas_notify_session_length(struct wpa_supplicant *wpa_s); +void wpas_notify_bss_tm_status(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c index d23b0094c440..947917bb05f4 100644 --- a/wpa_supplicant/op_classes.c +++ b/wpa_supplicant/op_classes.c @@ -208,17 +208,78 @@ enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel, static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, const struct oper_class_map *op_class) { int chan; size_t i; struct hostapd_hw_modes *mode; int found; + int z; + int freq2 = 0; + int freq5 = 0; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); if (!mode) return 0; + /* If we are configured to disable certain things, take that into + * account here. */ + if (ssid->freq_list && ssid->freq_list[0]) { + for (z = 0; ; z++) { + int f = ssid->freq_list[z]; + + if (f == 0) + break; /* end of list */ + if (f > 4000 && f < 6000) + freq5 = 1; + else if (f > 2400 && f < 2500) + freq2 = 1; + } + } else { + /* No frequencies specified, can use anything hardware supports. + */ + freq2 = freq5 = 1; + } + + if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5) + return 0; + if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2) + return 0; + +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) { + switch (op_class->op_class) { + case 83: + case 84: + case 104: + case 105: + case 116: + case 117: + case 119: + case 120: + case 122: + case 123: + case 126: + case 127: + case 128: + case 129: + case 130: + /* Disable >= 40 MHz channels if HT is disabled */ + return 0; + } + } +#endif /* CONFIG_HT_OVERRIDES */ + +#ifdef CONFIG_VHT_OVERRIDES + if (ssid->disable_vht) { + if (op_class->op_class >= 128 && op_class->op_class <= 130) { + /* Disable >= 80 MHz channels if VHT is disabled */ + return 0; + } + } +#endif /* CONFIG_VHT_OVERRIDES */ + if (op_class->op_class == 128) { u8 channels[] = { 42, 58, 106, 122, 138, 155 }; @@ -273,8 +334,9 @@ static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, } -size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, - size_t len) +size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + int freq, u8 *pos, size_t len) { struct wpabuf *buf; u8 op, current, chan; @@ -304,7 +366,7 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, wpabuf_put_u8(buf, current); for (op = 0; global_op_class[op].op_class; op++) { - if (wpas_op_class_supported(wpa_s, &global_op_class[op])) + if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op])) wpabuf_put_u8(buf, global_op_class[op].op_class); } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index c596d5ab6148..e7c1f5d5aca4 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -980,6 +980,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, os_free(wpa_s->p2p_group_common_freqs); wpa_s->p2p_group_common_freqs = NULL; wpa_s->p2p_group_common_freqs_num = 0; + wpa_s->p2p_go_do_acs = 0; wpa_s->waiting_presence_resp = 0; @@ -1584,20 +1585,27 @@ static int wpas_send_action_work(struct wpa_supplicant *wpa_s, static int wpas_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) + size_t len, unsigned int wait_time, int *scheduled) { struct wpa_supplicant *wpa_s = ctx; int listen_freq = -1, send_freq = -1; + if (scheduled) + *scheduled = 0; if (wpa_s->p2p_listen_work) listen_freq = wpa_s->p2p_listen_work->freq; if (wpa_s->p2p_send_action_work) send_freq = wpa_s->p2p_send_action_work->freq; if (listen_freq != (int) freq && send_freq != (int) freq) { - wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)", - listen_freq, send_freq); - return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, - len, wait_time); + int res; + + wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)", + listen_freq, send_freq, freq); + res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, + len, wait_time); + if (res == 0 && scheduled) + *scheduled = 1; + return res; } wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX"); @@ -1649,7 +1657,7 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, wpa_supplicant_ap_deinit(wpa_s); wpas_copy_go_neg_results(wpa_s, res); if (res->wps_method == WPS_PBC) { - wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); + wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1, 0); #ifdef CONFIG_WPS_NFC } else if (res->wps_method == WPS_NFC) { wpas_wps_start_nfc(wpa_s, res->peer_device_addr, @@ -1912,6 +1920,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, ssid->vht = params->vht; ssid->max_oper_chwidth = params->max_oper_chwidth; ssid->vht_center_freq2 = params->vht_center_freq2; + ssid->he = params->he; ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); @@ -2075,6 +2084,13 @@ static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, return -1; } + if (wpa_s->conf->p2p_interface_random_mac_addr) { + random_mac_addr(wpa_s->pending_interface_addr); + wpa_printf(MSG_DEBUG, "P2P: Generate random MAC address " MACSTR + " for the group", + MAC2STR(wpa_s->pending_interface_addr)); + } + if (force_ifname[0]) { wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s", force_ifname); @@ -2153,6 +2169,29 @@ wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) wpas_p2p_clone_config(group_wpa_s, wpa_s); + if (wpa_s->conf->p2p_interface_random_mac_addr) { + if (wpa_drv_set_mac_addr(group_wpa_s, + wpa_s->pending_interface_addr) < 0) { + wpa_msg(group_wpa_s, MSG_INFO, + "Failed to set random MAC address"); + wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s, + 0); + return NULL; + } + + if (wpa_supplicant_update_mac_addr(group_wpa_s) < 0) { + wpa_msg(group_wpa_s, MSG_INFO, + "Could not update MAC address information"); + wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s, + 0); + return NULL; + } + + wpa_printf(MSG_DEBUG, "P2P: Using random MAC address " MACSTR + " for the group", + MAC2STR(wpa_s->pending_interface_addr)); + } + return group_wpa_s; } @@ -3048,7 +3087,7 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, MAC2STR(sa), s->id); } wpas_p2p_group_add_persistent( - wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL, + wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, 0, NULL, go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 1); } else if (bssid) { @@ -3274,6 +3313,7 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, wpa_s->p2p_go_max_oper_chwidth, + wpa_s->p2p_go_he, channels, ssid->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : @@ -3918,6 +3958,10 @@ static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go, /* Remove stale persistent group */ if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove stale persistent group id=%d", + s->id); + wpas_notify_persistent_group_removed(wpa_s, s); wpa_config_remove_network(wpa_s->conf, s->id); save_config = 1; continue; @@ -4041,6 +4085,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (persistent_go && !persistent_go->num_p2p_clients) { /* remove empty persistent GO */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove empty persistent group id=%d", + persistent_go->id); + wpas_notify_persistent_group_removed(wpa_s, + persistent_go); wpa_config_remove_network(wpa_s->conf, persistent_go->id); } @@ -4081,6 +4130,10 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, /* Remove stale persistent group */ if (stale->mode != WPAS_MODE_P2P_GO || stale->num_p2p_clients <= 1) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove stale persistent group id=%d", + stale->id); + wpas_notify_persistent_group_removed(wpa_s, stale); wpa_config_remove_network(wpa_s->conf, stale->id); } else { size_t i; @@ -4113,6 +4166,11 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (persistent_go && s != persistent_go && !persistent_go->num_p2p_clients) { /* remove empty persistent GO */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove empty persistent group id=%d", + persistent_go->id); + wpas_notify_persistent_group_removed(wpa_s, + persistent_go); wpa_config_remove_network(wpa_s->conf, persistent_go->id); /* Save config */ @@ -4130,6 +4188,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, return; } + wpa_s->global->pending_p2ps_group = 0; + wpa_s->global->pending_p2ps_group_freq = 0; + if (conncap == P2PS_SETUP_GROUP_OWNER) { /* * We need to copy the interface name. Simply saving a @@ -4140,8 +4201,10 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, go_ifname[0] = '\0'; if (!go_wpa_s) { - wpa_s->global->pending_p2ps_group = 1; - wpa_s->global->pending_p2ps_group_freq = freq; + if (!response_done) { + wpa_s->global->pending_p2ps_group = 1; + wpa_s->global->pending_p2ps_group_freq = freq; + } if (!wpas_p2p_create_iface(wpa_s)) os_memcpy(go_ifname, wpa_s->ifname, @@ -4165,13 +4228,14 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (response_done && persistent_go) { wpas_p2p_group_add_persistent( wpa_s, persistent_go, - 0, 0, freq, 0, 0, 0, 0, NULL, + 0, 0, freq, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else if (response_done) { - wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, + 0, 0, 0, 0, 0); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { @@ -4220,6 +4284,10 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (persistent_go && !persistent_go->num_p2p_clients) { /* remove empty persistent GO */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove empty persistent group id=%d", + persistent_go->id); + wpas_notify_persistent_group_removed(wpa_s, persistent_go); wpa_config_remove_network(wpa_s->conf, persistent_go->id); } @@ -4283,11 +4351,11 @@ static int wpas_prov_disc_resp_cb(void *ctx) if (persistent_go) { wpas_p2p_group_add_persistent( - wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL, + wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else { - wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0); } return 1; @@ -4305,6 +4373,54 @@ static int wpas_p2p_get_pref_freq_list(void *ctx, int go, } +int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) +{ + u8 addr[ETH_ALEN] = {0}; + + if (wpa_s->conf->p2p_device_random_mac_addr == 0) + return 0; + + if (!wpa_s->conf->ssid) { + if (random_mac_addr(addr) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to generate random MAC address"); + return -EINVAL; + } + + /* Store generated MAC address. */ + os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr, addr, + ETH_ALEN); + } else { + /* If there are existing saved groups, restore last MAC address. + * if there is no last used MAC address, the last one is + * factory MAC. */ + if (is_zero_ether_addr( + wpa_s->conf->p2p_device_persistent_mac_addr)) + return 0; + os_memcpy(addr, wpa_s->conf->p2p_device_persistent_mac_addr, + ETH_ALEN); + wpa_msg(wpa_s, MSG_DEBUG, "Restore last used MAC address."); + } + + if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to set random MAC address"); + return -EINVAL; + } + + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not update MAC address information"); + return -EINVAL; + } + + wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR, + MAC2STR(addr)); + + return 0; +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -4325,6 +4441,12 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) if (global->p2p) return 0; + if (wpas_p2p_mac_setup(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to initialize P2P random MAC address."); + return -1; + } + os_memset(&p2p, 0, sizeof(p2p)); p2p.cb_ctx = wpa_s; p2p.debug_print = wpas_p2p_debug_print; @@ -4389,7 +4511,10 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) * channel. */ if (p2p_config_get_random_social(&p2p, &p2p.reg_class, - &p2p.channel) != 0) { + &p2p.channel, + &global->p2p_go_avoid_freq, + &global->p2p_disallow_freq) != + 0) { wpa_printf(MSG_INFO, "P2P: No social channels supported by the driver - do not enable P2P"); return 0; @@ -4414,10 +4539,14 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) * other preference is indicated. */ if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class, - &p2p.op_channel) != 0) { - wpa_printf(MSG_ERROR, + &p2p.op_channel, NULL, + NULL) != 0) { + wpa_printf(MSG_INFO, "P2P: Failed to select random social channel as operation channel"); - return -1; + p2p.op_reg_class = 0; + p2p.op_channel = 0; + /* This will be overridden during group setup in + * p2p_prepare_channel(), so allow setup to continue. */ } p2p.cfg_op_channel = 0; wpa_printf(MSG_DEBUG, "P2P: Random operating channel: " @@ -4810,6 +4939,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, wpa_s->p2p_go_max_oper_chwidth, + wpa_s->p2p_go_he, NULL, 0); return; } @@ -5107,17 +5237,18 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, os_memcpy(group->p2p_pin, wpa_s->p2p_pin, sizeof(group->p2p_pin)); group->p2p_wps_method = wpa_s->p2p_wps_method; - } else { - /* - * Need to mark the current interface for p2p_group_formation - * when a separate group interface is not used. This is needed - * to allow p2p_cancel stop a pending p2p_connect-join. - * wpas_p2p_init_group_interface() addresses this for the case - * where a separate group interface is used. - */ - wpa_s->global->p2p_group_formation = wpa_s; } + /* + * Need to mark the current interface for p2p_group_formation + * when a separate group interface is not used. This is needed + * to allow p2p_cancel stop a pending p2p_connect-join. + * wpas_p2p_init_group_interface() addresses this for the case + * where a separate group interface is used. + */ + if (group == wpa_s->parent) + wpa_s->global->p2p_group_formation = group; + group->p2p_in_provisioning = 1; group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg; @@ -5357,7 +5488,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int persistent_group, int auto_join, int join, int auth, int go_intent, int freq, unsigned int vht_center_freq2, int persistent_id, int pd, int ht40, int vht, - unsigned int vht_chwidth, const u8 *group_ssid, + unsigned int vht_chwidth, int he, const u8 *group_ssid, size_t group_ssid_len) { int force_freq = 0, pref_freq = 0; @@ -5402,6 +5533,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_go_vht = !!vht; wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; wpa_s->p2p_go_max_oper_chwidth = vht_chwidth; + wpa_s->p2p_go_he = !!he; if (pin) os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); @@ -5825,7 +5957,7 @@ static int wpas_same_band(int freq1, int freq2) static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, int freq, int vht_center_freq2, int ht40, - int vht, int max_oper_chwidth, + int vht, int max_oper_chwidth, int he, const struct p2p_channels *channels) { struct wpa_used_freq_data *freqs; @@ -5838,6 +5970,7 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, params->role_go = 1; params->ht40 = ht40; params->vht = vht; + params->he = he; params->max_oper_chwidth = max_oper_chwidth; params->vht_center_freq2 = vht_center_freq2; @@ -6194,7 +6327,7 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, */ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int vht_center_freq2, int ht40, int vht, - int max_oper_chwidth) + int max_oper_chwidth, int he) { struct p2p_go_neg_results params; @@ -6215,7 +6348,7 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, - ht40, vht, max_oper_chwidth, NULL)) + ht40, vht, max_oper_chwidth, he, NULL)) return -1; p2p_go_params(wpa_s->global->p2p, ¶ms); @@ -6294,7 +6427,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, int vht_center_freq2, int ht40, - int vht, int max_oper_chwidth, + int vht, int max_oper_chwidth, int he, const struct p2p_channels *channels, int connection_timeout, int force_scan) { @@ -6370,7 +6503,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, - ht40, vht, max_oper_chwidth, channels)) + ht40, vht, max_oper_chwidth, he, channels)) return -1; params.role_go = 1; @@ -6922,7 +7055,7 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, int vht_center_freq2, int ht40, int vht, int max_chwidth, - int pref_freq) + int pref_freq, int he) { enum p2p_invite_role role; u8 *bssid = NULL; @@ -6940,6 +7073,7 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_persistent_go_freq = freq; wpa_s->p2p_go_ht40 = !!ht40; wpa_s->p2p_go_vht = !!vht; + wpa_s->p2p_go_he = !!he; wpa_s->p2p_go_max_oper_chwidth = max_chwidth; wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; if (ssid->mode == WPAS_MODE_P2P_GO) { @@ -7086,7 +7220,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) u8 go_dev_addr[ETH_ALEN]; int persistent; int freq; - u8 ip[3 * 4]; + u8 ip[3 * 4], *ip_ptr = NULL; char ip_addr[100]; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { @@ -7133,6 +7267,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) ip[8], ip[9], ip[10], ip[11]); if (os_snprintf_error(sizeof(ip_addr), res)) ip_addr[0] = '\0'; + ip_ptr = ip; } wpas_p2p_group_started(wpa_s, 0, ssid, freq, @@ -7145,7 +7280,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) wpas_p2p_store_persistent_group(wpa_s->p2pdev, ssid, go_dev_addr); - wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip); + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr); } @@ -7962,7 +8097,8 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, - wpa_s->p2p_go_max_oper_chwidth, NULL, 0); + wpa_s->p2p_go_max_oper_chwidth, + wpa_s->p2p_go_he, NULL, 0); return ret; } @@ -8498,6 +8634,7 @@ static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, params->go_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + wpa_s->p2p_go_he, params->go_ssid_len ? params->go_ssid : NULL, params->go_ssid_len); } @@ -8577,7 +8714,7 @@ static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, forced_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, - NULL, 0); + wpa_s->p2p_go_he, NULL, 0); } @@ -8593,7 +8730,7 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, forced_freq, wpa_s->p2p_go_vht_center_freq2, -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, - NULL, 0); + wpa_s->p2p_go_he, NULL, 0); if (res) return res; @@ -8978,7 +9115,7 @@ static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) * TODO: This function may not always work correctly. For example, * when we have a running GO and a BSS on a DFS channel. */ - if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) { + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, 0, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P CSA: Failed to select new frequency for GO"); return -1; @@ -9090,7 +9227,7 @@ static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s) wpa_supplicant_ap_deinit(wpa_s); /* Reselect the GO frequency */ - if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) { + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, 0, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 63910d1c268e..24ec2cafc249 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -37,18 +37,18 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int persistent_group, int auto_join, int join, int auth, int go_intent, int freq, unsigned int vht_center_freq2, int persistent_id, int pd, int ht40, int vht, - unsigned int vht_chwidth, const u8 *group_ssid, + unsigned int vht_chwidth, int he, const u8 *group_ssid, size_t group_ssid_len); int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int vht_center_freq2, int ht40, int vht, - int max_oper_chwidth); + int max_oper_chwidth, int he); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, int force_freq, int neg_freq, int vht_center_freq2, int ht40, - int vht, int max_oper_chwidth, + int vht, int max_oper_chwidth, int he, const struct p2p_channels *channels, int connection_timeout, int force_scan); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, @@ -117,7 +117,7 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, int vht_center_freq2, int ht40, int vht, - int max_oper_chwidth, int pref_freq); + int max_oper_chwidth, int pref_freq, int he); int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, const u8 *peer_addr, const u8 *go_dev_addr); int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, @@ -211,6 +211,7 @@ int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, unsigned int period, unsigned int interval, unsigned int count); int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s); +int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s); #else /* CONFIG_P2P */ diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c index f4fbfa719352..cb3c6c995dd9 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -392,17 +392,66 @@ static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s, } +static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len) +{ + struct rrm_measurement_report_element *msr_rep; + u8 *end = pos + len; + u8 *msr_rep_end; + struct rrm_measurement_beacon_report *rep = NULL; + u8 *subelem; + + /* Find the last beacon report element */ + while (end - pos >= (int) sizeof(*msr_rep)) { + msr_rep = (struct rrm_measurement_report_element *) pos; + msr_rep_end = pos + msr_rep->len + 2; + + if (msr_rep->eid != WLAN_EID_MEASURE_REPORT || + msr_rep_end > end) { + /* Should not happen. This indicates a bug. */ + wpa_printf(MSG_ERROR, + "RRM: non-measurement report element in measurement report frame"); + return -1; + } + + if (msr_rep->type == MEASURE_TYPE_BEACON) + rep = (struct rrm_measurement_beacon_report *) + msr_rep->variable; + + pos += pos[1] + 2; + } + + if (!rep) + return 0; + + subelem = rep->variable; + while (subelem + 2 < msr_rep_end && + subelem[0] != WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION) + subelem += 2 + subelem[1]; + + if (subelem + 2 < msr_rep_end && + subelem[0] == WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION && + subelem[1] == 1 && + subelem + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end) + subelem[2] = 1; + + return 0; +} + + static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s, struct wpabuf *buf) { int len = wpabuf_len(buf); - const u8 *pos = wpabuf_head_u8(buf), *next = pos; + u8 *pos = wpabuf_mhead_u8(buf), *next = pos; #define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3) while (len) { int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len; + if (send_len == len) + wpas_rrm_beacon_rep_update_last_frame(pos, len); + if (send_len == len || (send_len + next[1] + 2) > MPDU_REPORT_LEN) { wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len); @@ -707,15 +756,17 @@ static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, enum beacon_report_detail detail, struct wpa_bss *bss, u8 *buf, - size_t buf_len) + size_t buf_len, u8 **ies_buf, + size_t *ie_len, int add_fixed) { - u8 *ies = (u8 *) (bss + 1); - size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + u8 *ies = *ies_buf; + size_t ies_len = *ie_len; u8 *pos = buf; int rem_len; rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) - - sizeof(struct rrm_measurement_report_element) - 2; + sizeof(struct rrm_measurement_report_element) - 2 - + REPORTED_FRAME_BODY_SUBELEM_LEN; if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { wpa_printf(MSG_DEBUG, @@ -731,18 +782,21 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) + * beacon interval(2) + capabilities(2) = 14 bytes */ - if (buf_len < 14) - return 0; + if (add_fixed && buf_len < 14) + return -1; *pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY; /* The length will be filled later */ pos++; - WPA_PUT_LE64(pos, bss->tsf); - pos += sizeof(bss->tsf); - WPA_PUT_LE16(pos, bss->beacon_int); - pos += 2; - WPA_PUT_LE16(pos, bss->caps); - pos += 2; + + if (add_fixed) { + WPA_PUT_LE64(pos, bss->tsf); + pos += sizeof(bss->tsf); + WPA_PUT_LE16(pos, bss->beacon_int); + pos += 2; + WPA_PUT_LE16(pos, bss->caps); + pos += 2; + } rem_len -= pos - buf; @@ -757,15 +811,7 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) { if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS || (eids && bitfield_is_set(eids, ies[0]))) { - u8 eid = ies[0], elen = ies[1]; - - if ((eid == WLAN_EID_TIM || eid == WLAN_EID_RSN) && - elen > 4) - elen = 4; - /* - * TODO: Truncate IBSS DFS element as described in - * IEEE Std 802.11-2016, 9.4.2.22.7. - */ + u8 elen = ies[1]; if (2 + elen > buf + buf_len - pos || 2 + elen > rem_len) @@ -782,22 +828,91 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, ies += 2 + ies[1]; } + *ie_len = ies_len; + *ies_buf = ies; + /* Now the length is known */ buf[1] = pos - buf - 2; return pos - buf; } +static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data, + struct wpa_bss *bss, + struct wpabuf **wpa_buf, + struct rrm_measurement_beacon_report *rep, + u8 **ie, size_t *ie_len, u8 idx) +{ + int ret; + u8 *buf, *pos; + u32 subelems_len = REPORTED_FRAME_BODY_SUBELEM_LEN + + (data->last_indication ? + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN : 0); + + /* Maximum element length: Beacon Report element + Reported Frame Body + * subelement + all IEs of the reported Beacon frame + Reported Frame + * Body Fragment ID subelement */ + buf = os_malloc(sizeof(*rep) + 14 + *ie_len + subelems_len); + if (!buf) + return -1; + + os_memcpy(buf, rep, sizeof(*rep)); + + ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, + bss, buf + sizeof(*rep), + 14 + *ie_len, ie, ie_len, + idx == 0); + if (ret < 0) + goto out; + + pos = buf + ret + sizeof(*rep); + pos[0] = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID; + pos[1] = 2; + + /* + * Only one Beacon Report Measurement is supported at a time, so + * the Beacon Report ID can always be set to 1. + */ + pos[2] = 1; + + /* Fragment ID Number (bits 0..6) and More Frame Body Fragments (bit 7) + */ + pos[3] = idx; + if (data->report_detail != BEACON_REPORT_DETAIL_NONE && *ie_len) + pos[3] |= REPORTED_FRAME_BODY_MORE_FRAGMENTS; + else + pos[3] &= ~REPORTED_FRAME_BODY_MORE_FRAGMENTS; + + pos += REPORTED_FRAME_BODY_SUBELEM_LEN; + + if (data->last_indication) { + pos[0] = WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION; + pos[1] = 1; + + /* This field will be updated later if this is the last frame */ + pos[2] = 0; + } + + ret = wpas_rrm_report_elem(wpa_buf, data->token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, buf, + ret + sizeof(*rep) + subelems_len); +out: + os_free(buf); + return ret; +} + + static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s, struct wpabuf **wpa_buf, struct wpa_bss *bss, u64 start, u64 parent_tsf) { struct beacon_rep_data *data = &wpa_s->beacon_rep_data; - u8 *ie = (u8 *) (bss + 1); - size_t ie_len = bss->ie_len + bss->beacon_ie_len; - int ret; - u8 *buf; - struct rrm_measurement_beacon_report *rep; + u8 *ies = (u8 *) (bss + 1); + u8 *pos = ies; + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + struct rrm_measurement_beacon_report rep; + u8 idx = 0; if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 && os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0) @@ -808,39 +923,29 @@ static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s, os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0)) return 0; - /* Maximum element length: beacon report element + reported frame body - * subelement + all IEs of the reported beacon */ - buf = os_malloc(sizeof(*rep) + 14 + ie_len); - if (!buf) - return -1; + if (wpas_get_op_chan_phy(bss->freq, ies, ies_len, &rep.op_class, + &rep.channel, &rep.report_info) < 0) + return 0; - rep = (struct rrm_measurement_beacon_report *) buf; - if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class, - &rep->channel, &rep->report_info) < 0) { - ret = 0; - goto out; - } + rep.start_time = host_to_le64(start); + rep.duration = host_to_le16(data->scan_params.duration); + rep.rcpi = rssi_to_rcpi(bss->level); + rep.rsni = 255; /* 255 indicates that RSNI is not available */ + os_memcpy(rep.bssid, bss->bssid, ETH_ALEN); + rep.antenna_id = 0; /* unknown */ + rep.parent_tsf = host_to_le32(parent_tsf); - rep->start_time = host_to_le64(start); - rep->duration = host_to_le16(data->scan_params.duration); - rep->rcpi = rssi_to_rcpi(bss->level); - rep->rsni = 255; /* 255 indicates that RSNI is not available */ - os_memcpy(rep->bssid, bss->bssid, ETH_ALEN); - rep->antenna_id = 0; /* unknown */ - rep->parent_tsf = host_to_le32(parent_tsf); + do { + int ret; - ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, - bss, rep->variable, 14 + ie_len); - if (ret < 0) - goto out; + ret = wpas_add_beacon_rep_elem(data, bss, wpa_buf, &rep, + &pos, &ies_len, idx++); + if (ret) + return ret; + } while (data->report_detail != BEACON_REPORT_DETAIL_NONE && + ies_len >= 2); - ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token, - MEASUREMENT_REPORT_MODE_ACCEPT, - MEASURE_TYPE_BEACON, buf, - ret + sizeof(*rep)); -out: - os_free(buf); - return ret; + return 0; } @@ -1006,6 +1111,16 @@ static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s, case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL: /* Skip - it will be processed when freqs are added */ break; + case WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Beacon request: Invalid last indication request subelement length: %u", + slen); + return -1; + } + + data->last_indication = subelem[0]; + break; default: wpa_printf(MSG_DEBUG, "Beacon request: Unknown subelement id %u", sid); diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index ee39e0c9228d..7abb028dd344 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2014, Jouni Malinen + * Copyright (c) 2003-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -581,8 +581,8 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS */ #ifdef CONFIG_HS20 - if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0) - wpas_hs20_add_indication(extra_ie, -1); + if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0) + wpas_hs20_add_indication(extra_ie, -1, 0); #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST @@ -1993,7 +1993,8 @@ static int wpa_scan_result_compar(const void *a, const void *b) /* if SNR is close, decide by max rate or frequency band */ if (snr_a && snr_b && abs(snr_b - snr_a) < 7) { if (wa->est_throughput != wb->est_throughput) - return wb->est_throughput - wa->est_throughput; + return (int) wb->est_throughput - + (int) wa->est_throughput; } if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { @@ -2806,6 +2807,13 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, { u8 *tmp = NULL; + if ((wpa_s->mac_addr_rand_supported & type) != type ) { + wpa_printf(MSG_INFO, + "scan: MAC randomization type %u != supported=%u", + type, wpa_s->mac_addr_rand_supported); + return -1; + } + wpas_mac_addr_rand_scan_clear(wpa_s, type); if (addr) { diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 39c80696a94c..17a984d1a17d 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -12,9 +12,11 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/ocv.h" #include "eapol_supp/eapol_supp_sm.h" #include "common/wpa_common.h" #include "common/sae.h" +#include "common/dpp.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" #include "config.h" @@ -56,7 +58,7 @@ static int index_within_array(const int *array, int idx) static int sme_set_sae_group(struct wpa_supplicant *wpa_s) { int *groups = wpa_s->conf->sae_groups; - int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + int default_groups[] = { 19, 20, 21, 0 }; if (!groups || groups[0] <= 0) groups = default_groups; @@ -83,7 +85,8 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s) static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - const u8 *bssid, int external) + const u8 *bssid, int external, + int reuse) { struct wpabuf *buf; size_t len; @@ -95,8 +98,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override)); if (!buf) return NULL; - wpabuf_put_le16(buf, 1); /* Transaction seq# */ - wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + if (!external) { + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } wpabuf_put_buf(buf, wpa_s->sae_commit_override); return buf; } @@ -110,6 +115,12 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, return NULL; } + if (reuse && wpa_s->sme.sae.tmp && + os_memcmp(bssid, wpa_s->sme.sae.tmp->bssid, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "SAE: Reuse previously generated PWE on a retry with the same AP"); + goto reuse_data; + } if (sme_set_sae_group(wpa_s) < 0) { wpa_printf(MSG_DEBUG, "SAE: Failed to select group"); return NULL; @@ -122,7 +133,10 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } + if (wpa_s->sme.sae.tmp) + os_memcpy(wpa_s->sme.sae.tmp->bssid, bssid, ETH_ALEN); +reuse_data: len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0; if (ssid->sae_password_id) len += 4 + os_strlen(ssid->sae_password_id); @@ -302,6 +316,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (!rsn) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE enabled, but target BSS does not advertise RSN"); +#ifdef CONFIG_DPP + } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && + (ssid->key_mgmt & WPA_KEY_MGMT_DPP) && + (ied.key_mgmt & WPA_KEY_MGMT_DPP)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Prefer DPP over SAE when both are enabled"); +#endif /* CONFIG_DPP */ } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && wpa_key_mgmt_sae(ied.key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); @@ -434,13 +454,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) md = ie + 2; wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); + if (md && (!wpa_key_mgmt_ft(ssid->key_mgmt) || + !wpa_key_mgmt_ft(wpa_s->key_mgmt))) + md = NULL; if (md) { /* Prepare for the next transition */ wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } - if (md && !wpa_key_mgmt_ft(ssid->key_mgmt)) - md = NULL; if (md) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x", md[0], md[1]); @@ -459,7 +480,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_s->sme.assoc_req_ie_len += 5; } - if (wpa_s->sme.ft_used && + if (wpa_s->sme.prev_bssid_set && wpa_s->sme.ft_used && os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 && wpa_sm_has_ptk(wpa_s->wpa)) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT " @@ -519,7 +540,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, sme_auth_handle_rrm(wpa_s, bss); wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie( - wpa_s, bss->freq, + wpa_s, ssid, bss->freq, wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len); @@ -550,7 +571,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); size_t len; - wpas_hs20_add_indication(hs20, pps_mo_id); + wpas_hs20_add_indication(hs20, pps_mo_id, + get_hs20_version(bss)); wpas_hs20_add_roam_cons_sel(hs20, ssid); len = sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len; @@ -618,7 +640,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0, - NULL, WPA_KEY_MGMT_SAE) == 0) { + NULL, + wpa_s->key_mgmt == WPA_KEY_MGMT_FT_SAE ? + WPA_KEY_MGMT_FT_SAE : + WPA_KEY_MGMT_SAE) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication"); wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -629,7 +654,8 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) { if (start) resp = sme_auth_build_sae_commit(wpa_s, ssid, - bss->bssid, 0); + bss->bssid, 0, + start == 2); else resp = sme_auth_build_sae_confirm(wpa_s, 0); if (resp == NULL) { @@ -912,7 +938,7 @@ static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, { struct wpabuf *resp, *buf; - resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1); + resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1, 0); if (!resp) return; @@ -939,10 +965,9 @@ static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, os_memset(¶ms, 0, sizeof(params)); params.status = status; - os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid, - wpa_s->sme.ext_auth.ssid_len); + params.ssid = wpa_s->sme.ext_auth.ssid; params.ssid_len = wpa_s->sme.ext_auth.ssid_len; - os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN); + params.bssid = wpa_s->sme.ext_auth.bssid; wpa_drv_send_external_auth_status(wpa_s, ¶ms); } @@ -952,14 +977,14 @@ static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; size_t ssid_str_len = data->external_auth.ssid_len; - u8 *ssid_str = data->external_auth.ssid; + const u8 *ssid_str = data->external_auth.ssid; /* Get the SSID conf from the ssid string obtained */ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { if (!wpas_network_disabled(wpa_s, ssid) && ssid_str_len == ssid->ssid_len && os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 && - (ssid->key_mgmt & WPA_KEY_MGMT_SAE)) + (ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))) break; } if (ssid) @@ -1035,7 +1060,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && wpa_s->sme.sae.state == SAE_COMMITTED && (external || wpa_s->current_bss) && wpa_s->current_ssid) { - int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + int default_groups[] = { 19, 20, 21, 0 }; u16 group; groups = wpa_s->conf->sae_groups; @@ -1063,7 +1088,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, len - sizeof(le16)); if (!external) sme_send_authentication(wpa_s, wpa_s->current_bss, - wpa_s->current_ssid, 1); + wpa_s->current_ssid, 2); else sme_external_auth_send_sae_commit( wpa_s, wpa_s->sme.ext_auth.bssid, @@ -1386,7 +1411,6 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } -#ifdef CONFIG_FILS #ifdef CONFIG_IEEE80211R static void remove_ie(u8 *buf, size_t *len, u8 eid) { @@ -1401,7 +1425,6 @@ static void remove_ie(u8 *buf, size_t *len, u8 eid) } } #endif /* CONFIG_IEEE80211R */ -#endif /* CONFIG_FILS */ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, @@ -1554,6 +1577,52 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP && wpa_s->current_ssid && + wpa_s->current_ssid->dpp_netaccesskey) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len); + if (!wpa_s->dpp_pfs) { + wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + if (wpa_s->sme.assoc_req_ie_len + + wpabuf_len(wpa_s->dpp_pfs->ie) > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_ERROR, + "DPP: Not enough buffer room for own Association Request frame elements"); + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = NULL; + goto pfs_fail; + } + os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(wpa_s->dpp_pfs->ie), + wpabuf_len(wpa_s->dpp_pfs->ie)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie); + } +pfs_fail: +#endif /* CONFIG_DPP2 */ + + if (wpa_s->current_ssid && wpa_s->current_ssid->multi_ap_backhaul_sta) { + size_t multi_ap_ie_len; + + multi_ap_ie_len = add_multi_ap_ie( + wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len, + MULTI_AP_BACKHAUL_STA); + if (multi_ap_ie_len == 0) { + wpa_printf(MSG_ERROR, + "Multi-AP: Failed to build Multi-AP IE"); + return; + } + wpa_s->sme.assoc_req_ie_len += multi_ap_ie_len; + } + params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; @@ -1916,17 +1985,14 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used) sme_update_ft_ies(wpa_s, NULL, NULL, 0); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + sme_stop_sa_query(wpa_s); +#endif /* CONFIG_IEEE80211W */ } void sme_deinit(struct wpa_supplicant *wpa_s) { - os_free(wpa_s->sme.ft_ies); - wpa_s->sme.ft_ies = NULL; - wpa_s->sme.ft_ies_len = 0; -#ifdef CONFIG_IEEE80211W - sme_stop_sa_query(wpa_s); -#endif /* CONFIG_IEEE80211W */ sme_clear_on_disassoc(wpa_s); eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); @@ -2220,6 +2286,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) static const unsigned int sa_query_max_timeout = 1000; static const unsigned int sa_query_retry_timeout = 201; +static const unsigned int sa_query_ch_switch_max_delay = 5000; /* in usec */ static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) { @@ -2243,7 +2310,9 @@ static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, const u8 *trans_id) { - u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN]; + u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN]; + u8 req_len = 2 + WLAN_SA_QUERY_TR_ID_LEN; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to " MACSTR, MAC2STR(wpa_s->bssid)); wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID", @@ -2251,9 +2320,27 @@ static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s, req[0] = WLAN_ACTION_SA_QUERY; req[1] = WLAN_SA_QUERY_REQUEST; os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN); + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(wpa_s->wpa)) { + struct wpa_channel_info ci; + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Request frame"); + return; + } + + if (ocv_insert_extended_oci(&ci, req + req_len) < 0) + return; + + req_len += OCV_OCI_EXTENDED_LEN; + } +#endif /* CONFIG_OCV */ + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, - req, sizeof(req), 0) < 0) + req, req_len, 0) < 0) wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query " "Request"); } @@ -2310,6 +2397,8 @@ static void sme_start_sa_query(struct wpa_supplicant *wpa_s) static void sme_stop_sa_query(struct wpa_supplicant *wpa_s) { + if (wpa_s->sme.sa_query_trans_id) + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Stop SA Query"); eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); os_free(wpa_s->sme.sa_query_trans_id); wpa_s->sme.sa_query_trans_id = NULL; @@ -2348,15 +2437,74 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, } -void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, - const u8 *data, size_t len) +void sme_event_ch_switch(struct wpa_supplicant *wpa_s) +{ + unsigned int usec; + u32 _rand; + + if (wpa_s->wpa_state != WPA_COMPLETED || + !wpa_sm_ocv_enabled(wpa_s->wpa)) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Channel switch completed - trigger new SA Query to verify new operating channel"); + sme_stop_sa_query(wpa_s); + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); + usec = _rand % (sa_query_ch_switch_max_delay + 1); + eloop_register_timeout(0, usec, sme_sa_query_timer, wpa_s, NULL); +} + + +static void sme_process_sa_query_request(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, + size_t len) +{ + u8 resp[2 + WLAN_SA_QUERY_TR_ID_LEN + OCV_OCI_EXTENDED_LEN]; + u8 resp_len = 2 + WLAN_SA_QUERY_TR_ID_LEN; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Response to " + MACSTR, MAC2STR(wpa_s->bssid)); + + resp[0] = WLAN_ACTION_SA_QUERY; + resp[1] = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp + 2, data + 1, WLAN_SA_QUERY_TR_ID_LEN); + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(wpa_s->wpa)) { + struct wpa_channel_info ci; + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in SA Query Response frame"); + return; + } + + if (ocv_insert_extended_oci(&ci, resp + resp_len) < 0) + return; + + resp_len += OCV_OCI_EXTENDED_LEN; + } +#endif /* CONFIG_OCV */ + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + resp, resp_len, 0) < 0) + wpa_msg(wpa_s, MSG_INFO, + "SME: Failed to send SA Query Response"); +} + + +static void sme_process_sa_query_response(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, + size_t len) { int i; - if (wpa_s->sme.sa_query_trans_id == NULL || - len < 1 + WLAN_SA_QUERY_TR_ID_LEN || - data[0] != WLAN_SA_QUERY_RESPONSE) + if (!wpa_s->sme.sa_query_trans_id) return; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from " MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); @@ -2381,4 +2529,48 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, sme_stop_sa_query(wpa_s); } + +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *data, size_t len) +{ + if (len < 1 + WLAN_SA_QUERY_TR_ID_LEN) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query frame from " + MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]); + +#ifdef CONFIG_OCV + if (wpa_sm_ocv_enabled(wpa_s->wpa)) { + struct ieee802_11_elems elems; + struct wpa_channel_info ci; + + if (ieee802_11_parse_elems(data + 1 + WLAN_SA_QUERY_TR_ID_LEN, + len - 1 - WLAN_SA_QUERY_TR_ID_LEN, + &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "SA Query: Failed to parse elements"); + return; + } + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info to validate received OCI in SA Query Action frame"); + return; + } + + if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_printf(MSG_WARNING, "%s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + + if (data[0] == WLAN_SA_QUERY_REQUEST) + sme_process_sa_query_request(wpa_s, sa, data, len); + else if (data[0] == WLAN_SA_QUERY_RESPONSE) + sme_process_sa_query_response(wpa_s, sa, data, len); +} + #endif /* CONFIG_IEEE80211W */ diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index f3c822025574..1a7f9e8320c7 100644 --- a/wpa_supplicant/sme.h +++ b/wpa_supplicant/sme.h @@ -28,6 +28,7 @@ void sme_event_disassoc(struct wpa_supplicant *wpa_s, struct disassoc_info *info); void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *da, u16 reason_code); +void sme_event_ch_switch(struct wpa_supplicant *wpa_s); void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *data, size_t len); void sme_state_changed(struct wpa_supplicant *wpa_s); @@ -89,6 +90,10 @@ static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, { } +static inline void sme_event_ch_switch(struct wpa_supplicant *wpa_s) +{ +} + static inline void sme_state_changed(struct wpa_supplicant *wpa_s) { } diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in index bc5d49af8655..75a37a8cdbd3 100644 --- a/wpa_supplicant/systemd/wpa_supplicant.service.in +++ b/wpa_supplicant/systemd/wpa_supplicant.service.in @@ -5,9 +5,9 @@ Wants=network.target [Service] Type=dbus -BusName=@DBUS_INTERFACE@ +BusName=fi.w1.wpa_supplicant1 ExecStart=@BINDIR@/wpa_supplicant -u [Install] WantedBy=multi-user.target -Alias=dbus-@DBUS_INTERFACE@.service +Alias=dbus-fi.w1.wpa_supplicant1.service diff --git a/wpa_supplicant/utils/log2pcap.py b/wpa_supplicant/utils/log2pcap.py index 65e2fa109cbb..141aecbe5178 100755 --- a/wpa_supplicant/utils/log2pcap.py +++ b/wpa_supplicant/utils/log2pcap.py @@ -28,7 +28,7 @@ def pcap_addpacket(pcap_file, ts, data): input = sys.argv[1] pcap = sys.argv[2] except IndexError: - print "Usage: %s " % sys.argv[0] + print("Usage: %s " % sys.argv[0]) sys.exit(2) input_file = open(input, 'r') diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c index a88cc46f3956..38800cc77fb7 100644 --- a/wpa_supplicant/wmm_ac.c +++ b/wpa_supplicant/wmm_ac.c @@ -471,13 +471,8 @@ static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, return -1; } - if (!ies) { - wpa_printf(MSG_ERROR, "WMM AC: Missing IEs"); - return -1; - } - - if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) { - wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration"); + if (!ies || !(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) { + /* WMM AC not in use for this connection */ return -1; } @@ -522,7 +517,7 @@ static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) for (i = 0; i < WMM_AC_NUM; i++) wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); - /* delete pending add_ts requset */ + /* delete pending add_ts request */ wmm_ac_del_req(wpa_s, 1); os_free(wpa_s->wmm_ac_assoc_info); diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 6b68fc9e3772..22a21361ad8a 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -12,6 +12,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "common/ocv.h" #include "rsn_supp/wpa.h" #include "config.h" #include "wpa_supplicant_i.h" @@ -20,6 +21,7 @@ #include "ctrl_iface.h" #include "bss.h" #include "wnm_sta.h" +#include "notify.h" #include "hs20_supplicant.h" #define MAX_TFS_IE_LEN 1024 @@ -58,8 +60,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, int res; size_t len; struct wnm_sleep_element *wnmsleep_ie; - u8 *wnmtfs_ie; - u8 wnmsleep_ie_len; + u8 *wnmtfs_ie, *oci_ie; + u8 wnmsleep_ie_len, oci_ie_len; u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : WNM_SLEEP_TFS_REQ_IE_NONE; @@ -106,7 +108,41 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", (u8 *) wnmtfs_ie, wnmtfs_ie_len); - mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); + oci_ie = NULL; + oci_ie_len = 0; +#ifdef CONFIG_OCV + if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) { + struct wpa_channel_info ci; + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_printf(MSG_WARNING, + "Failed to get channel info for OCI element in WNM-Sleep Mode frame"); + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + return -1; + } + + oci_ie_len = OCV_OCI_EXTENDED_LEN; + oci_ie = os_zalloc(oci_ie_len); + if (!oci_ie) { + wpa_printf(MSG_WARNING, + "Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame"); + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + return -1; + } + + if (ocv_insert_extended_oci(&ci, oci_ie) < 0) { + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + os_free(oci_ie); + return -1; + } + } +#endif /* CONFIG_OCV */ + + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len + + oci_ie_len); if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Request action frame"); @@ -131,8 +167,16 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); } +#ifdef CONFIG_OCV + /* copy OCV OCI here */ + if (oci_ie_len > 0) { + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + + wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len); + } +#endif /* CONFIG_OCV */ + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + - wnmtfs_ie_len; + wnmtfs_ie_len + oci_ie_len; res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, @@ -145,6 +189,7 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, os_free(wnmsleep_ie); os_free(wnmtfs_ie); + os_free(oci_ie); os_free(mgmt); return res; @@ -256,6 +301,10 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, /* multiple TFS Resp IE (assuming consecutive) */ const u8 *tfsresp_ie_start = NULL; const u8 *tfsresp_ie_end = NULL; +#ifdef CONFIG_OCV + const u8 *oci_ie = NULL; + u8 oci_ie_len = 0; +#endif /* CONFIG_OCV */ size_t left; if (!wpa_s->wnmsleep_used) { @@ -289,6 +338,12 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, if (!tfsresp_ie_start) tfsresp_ie_start = pos; tfsresp_ie_end = pos; +#ifdef CONFIG_OCV + } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 && + pos[2] == WLAN_EID_EXT_OCV_OCI) { + oci_ie = pos + 3; + oci_ie_len = ie_len - 1; +#endif /* CONFIG_OCV */ } else wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); pos += ie_len + 2; @@ -299,6 +354,26 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_OCV + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT && + wpa_sm_ocv_enabled(wpa_s->wpa)) { + struct wpa_channel_info ci; + + if (wpa_drv_channel_info(wpa_s, &ci) != 0) { + wpa_msg(wpa_s, MSG_WARNING, + "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame"); + return; + } + + if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci, + channel_width_to_int(ci.chanwidth), + ci.seg1_idx) != 0) { + wpa_msg(wpa_s, MSG_WARNING, "WNM: %s", ocv_errorstr); + return; + } + } +#endif /* CONFIG_OCV */ + wpa_s->wnmsleep_used = 0; if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || @@ -696,7 +771,7 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, continue; } - if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) { + if (wpa_is_bss_tmp_disallowed(wpa_s, target)) { wpa_printf(MSG_DEBUG, "MBO: Candidate BSS " MACSTR " retry delay is not over yet", @@ -941,6 +1016,9 @@ static void wnm_send_bss_transition_mgmt_resp( return; } + wpa_s->bss_tm_status = status; + wpas_notify_bss_tm_status(wpa_s); + wpabuf_put_u8(buf, WLAN_ACTION_WNM); wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP); wpabuf_put_u8(buf, dialog_token); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 779355440a01..695fcbe04995 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2018, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,7 +29,7 @@ static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2018, Jouni Malinen and contributors"; +"Copyright (c) 2004-2019, Jouni Malinen and contributors"; #define VENDOR_ELEM_FRAME_ID \ " 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \ @@ -49,6 +49,7 @@ static int wpa_cli_last_id = 0; static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; static const char *client_socket_dir = NULL; static char *ctrl_ifname = NULL; +static const char *global = NULL; static const char *pid_file = NULL; static const char *action_file = NULL; static int ping_interval = 5; @@ -74,6 +75,7 @@ static char ** wpa_list_cmd_list(void); static void update_creds(struct wpa_ctrl *ctrl); static void update_networks(struct wpa_ctrl *ctrl); static void update_stations(struct wpa_ctrl *ctrl); +static void update_ifnames(struct wpa_ctrl *ctrl); static void usage(void) @@ -1203,6 +1205,39 @@ static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_psk_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid PSK_PASSPHRASE command: needs two arguments (network id and PSK/passphrase)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PSK_PASSPHRASE-%s:%s", + argv[0], argv[1]); + if (os_snprintf_error(end - pos, ret)) { + printf("Too long PSK_PASSPHRASE command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (os_snprintf_error(end - pos, ret)) { + printf("Too long PSK_PASSPHRASE command.\n"); + return -1; + } + pos += ret; + } + + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1376,9 +1411,11 @@ static const char *network_fields[] = { "eap", "identity", "anonymous_identity", "password", "ca_cert", "ca_path", "client_cert", "private_key", "private_key_passwd", "dh_file", "subject_match", "altsubject_match", + "check_cert_subject", "domain_suffix_match", "domain_match", "ca_cert2", "ca_path2", "client_cert2", "private_key2", "private_key2_passwd", "dh_file2", "subject_match2", "altsubject_match2", + "check_cert_subject2", "domain_suffix_match2", "domain_match2", "phase1", "phase2", "pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id", "pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id", @@ -1412,7 +1449,7 @@ static const char *network_fields[] = { #ifdef CONFIG_HT_OVERRIDES "disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc", "ht40_intolerant", "disable_max_amsdu", "ampdu_factor", - "ampdu_density", "ht_mcs", + "ampdu_density", "ht_mcs", "rx_stbc", "tx_stbc", #endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_VHT_OVERRIDES "disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1", @@ -1426,6 +1463,8 @@ static const char *network_fields[] = { #ifdef CONFIG_MACSEC "macsec_policy", "macsec_integ_only", + "macsec_replay_protect", + "macsec_replay_window", "macsec_port", "mka_priority", #endif /* CONFIG_MACSEC */ @@ -2955,6 +2994,13 @@ static int wpa_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv); +} + + static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -3084,6 +3130,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_sensitive, " = configure one-time-password for an SSID" }, + { "psk_passphrase", wpa_cli_cmd_psk_passphrase, + wpa_cli_complete_network_id, cli_cmd_flag_sensitive, + " = configure PSK/passphrase for an SSID" }, { "passphrase", wpa_cli_cmd_passphrase, wpa_cli_complete_network_id, cli_cmd_flag_sensitive, " = configure private key passphrase\n" @@ -3614,6 +3663,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "dpp_configurator_get_key", wpa_cli_cmd_dpp_configurator_get_key, NULL, cli_cmd_flag_none, " = Get DPP configurator's private key" }, + { "dpp_configurator_sign", wpa_cli_cmd_dpp_configurator_sign, NULL, + cli_cmd_flag_none, + "conf= configurator= = generate self DPP configuration" }, { "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL, cli_cmd_flag_sensitive, "add PKEX code" }, @@ -3972,10 +4024,46 @@ static void wpa_cli_action_cb(char *msg, size_t len) #endif /* CONFIG_ANSI_C_EXTRA */ +static int wpa_cli_open_global_ctrl(void) +{ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + ctrl_conn = wpa_ctrl_open(NULL); +#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + ctrl_conn = wpa_ctrl_open(global); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + if (!ctrl_conn) { + fprintf(stderr, + "Failed to connect to wpa_supplicant global interface: %s error: %s\n", + global, strerror(errno)); + return -1; + } + + if (interactive) { + update_ifnames(ctrl_conn); + mon_conn = wpa_ctrl_open(global); + if (mon_conn) { + if (wpa_ctrl_attach(mon_conn) == 0) { + wpa_cli_attached = 1; + eloop_register_read_sock( + wpa_ctrl_get_fd(mon_conn), + wpa_cli_mon_receive, + NULL, NULL); + } else { + printf("Failed to open monitor connection through global control interface\n"); + } + } + update_stations(ctrl_conn); + } + + return 0; +} + + static void wpa_cli_reconnect(void) { wpa_cli_close_connection(); - if (wpa_cli_open_connection(ctrl_ifname, 1) < 0) + if ((global && wpa_cli_open_global_ctrl() < 0) || + (!global && wpa_cli_open_connection(ctrl_ifname, 1) < 0)) return; if (interactive) { @@ -4534,7 +4622,6 @@ int main(int argc, char *argv[]) int c; int daemonize = 0; int ret = 0; - const char *global = NULL; if (os_program_init()) return -1; @@ -4589,38 +4676,8 @@ int main(int argc, char *argv[]) if (eloop_init()) return -1; - if (global) { -#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE - ctrl_conn = wpa_ctrl_open(NULL); -#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */ - ctrl_conn = wpa_ctrl_open(global); -#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ - if (ctrl_conn == NULL) { - fprintf(stderr, "Failed to connect to wpa_supplicant " - "global interface: %s error: %s\n", - global, strerror(errno)); - return -1; - } - - if (interactive) { - update_ifnames(ctrl_conn); - mon_conn = wpa_ctrl_open(global); - if (mon_conn) { - if (wpa_ctrl_attach(mon_conn) == 0) { - wpa_cli_attached = 1; - eloop_register_read_sock( - wpa_ctrl_get_fd(mon_conn), - wpa_cli_mon_receive, - NULL, NULL); - } else { - printf("Failed to open monitor " - "connection through global " - "control interface\n"); - } - } - update_stations(ctrl_conn); - } - } + if (global && wpa_cli_open_global_ctrl() < 0) + return -1; eloop_register_signal_terminate(wpa_cli_terminate, NULL); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index e587d7e3cd69..96a3691cf3cf 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2018, Jouni Malinen + * Copyright (c) 2003-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -39,6 +39,7 @@ #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" #include "common/gas_server.h" +#include "common/dpp.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "blacklist.h" @@ -68,7 +69,7 @@ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2018, Jouni Malinen and contributors"; +"Copyright (c) 2003-2019, Jouni Malinen and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -444,7 +445,7 @@ void free_hw_features(struct wpa_supplicant *wpa_s) } -static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) +void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) { struct wpa_bss_tmp_disallowed *bss, *prev; @@ -672,6 +673,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #ifdef CONFIG_DPP wpas_dpp_deinit(wpa_s); + dpp_global_deinit(wpa_s->dpp); + wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ } @@ -846,6 +849,23 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_supplicant_state_txt(state)); + if (state == WPA_COMPLETED && + os_reltime_initialized(&wpa_s->roam_start)) { + os_reltime_age(&wpa_s->roam_start, &wpa_s->roam_time); + wpa_s->roam_start.sec = 0; + wpa_s->roam_start.usec = 0; + wpas_notify_auth_changed(wpa_s); + wpas_notify_roam_time(wpa_s); + wpas_notify_roam_complete(wpa_s); + } else if (state == WPA_DISCONNECTED && + os_reltime_initialized(&wpa_s->roam_start)) { + wpa_s->roam_start.sec = 0; + wpa_s->roam_start.usec = 0; + wpa_s->roam_time.sec = 0; + wpa_s->roam_time.usec = 0; + wpas_notify_roam_complete(wpa_s); + } + if (state == WPA_INTERFACE_DISABLED) { /* Assure normal scan when interface is restored */ wpa_s->normal_scans = 0; @@ -941,7 +961,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_supplicant_stop_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ - if (state == WPA_AUTHENTICATING) + if (state > WPA_SCANNING) wpa_supplicant_stop_autoscan(wpa_s); if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) @@ -1172,6 +1192,18 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, } +static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie, + int freq) +{ + if (!ie->has_group) + ie->group_cipher = wpa_default_rsn_cipher(freq); + if (!ie->has_pairwise) + ie->pairwise_cipher = wpa_default_rsn_cipher(freq); + return (ie->group_cipher & ssid->group_cipher) && + (ie->pairwise_cipher & ssid->pairwise_cipher); +} + + /** * wpa_supplicant_set_suites - Set authentication and encryption parameters * @wpa_s: Pointer to wpa_supplicant data @@ -1203,8 +1235,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && - (ie.group_cipher & ssid->group_cipher) && - (ie.pairwise_cipher & ssid->pairwise_cipher) && + matching_ciphers(ssid, &ie, bss->freq) && (ie.key_mgmt & ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); proto = WPA_PROTO_RSN; @@ -1344,6 +1375,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->pairwise_cipher = WPA_CIPHER_NONE; #else /* CONFIG_NO_WPA */ sel = ie.group_cipher & ssid->group_cipher; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP group 0x%x network profile group 0x%x; available group 0x%x", + ie.group_cipher, ssid->group_cipher, sel); wpa_s->group_cipher = wpa_pick_group_cipher(sel); if (wpa_s->group_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " @@ -1354,6 +1388,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_cipher_txt(wpa_s->group_cipher)); sel = ie.pairwise_cipher & ssid->pairwise_cipher; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP pairwise 0x%x network profile pairwise 0x%x; available pairwise 0x%x", + ie.pairwise_cipher, ssid->pairwise_cipher, sel); wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1); if (wpa_s->pairwise_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " @@ -1365,11 +1402,29 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_WPA */ sel = ie.key_mgmt & ssid->key_mgmt; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP key_mgmt 0x%x network profile key_mgmt 0x%x; available key_mgmt 0x%x", + ie.key_mgmt, ssid->key_mgmt, sel); #ifdef CONFIG_SAE if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ if (0) { +#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SHA384 + } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT FT/802.1X-SHA384"); + if (pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT is not fully functional, so + * disable the case for now. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: Disable PMKSA caching for FT/802.1X connection"); + pmksa_cache_clear_current(wpa_s->wpa); + } +#endif /* CONFIG_SHA384 */ +#endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SUITEB192 } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; @@ -1399,19 +1454,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256"); #endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R -#ifdef CONFIG_SHA384 - } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { - wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: using KEY_MGMT FT/802.1X-SHA384"); - if (pmksa_cache_get_current(wpa_s->wpa)) { - /* PMKSA caching with FT is not fully functional, so - * disable the case for now. */ - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: Disable PMKSA caching for FT/802.1X connection"); - pmksa_cache_clear_current(wpa_s->wpa); - } -#endif /* CONFIG_SHA384 */ } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); @@ -1422,18 +1464,25 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, "WPA: Disable PMKSA caching for FT/802.1X connection"); pmksa_cache_clear_current(wpa_s->wpa); } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_DPP + } else if (sel & WPA_KEY_MGMT_DPP) { + wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); +#endif /* CONFIG_DPP */ +#ifdef CONFIG_SAE + } else if (sel & WPA_KEY_MGMT_FT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE"); + } else if (sel & WPA_KEY_MGMT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); +#endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); #endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_SAE - } else if (sel & WPA_KEY_MGMT_SAE) { - wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); - } else if (sel & WPA_KEY_MGMT_FT_SAE) { - wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE; - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE"); -#endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211W } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; @@ -1463,11 +1512,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_OWE; wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE"); #endif /* CONFIG_OWE */ -#ifdef CONFIG_DPP - } else if (sel & WPA_KEY_MGMT_DPP) { - wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; - wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); -#endif /* CONFIG_DPP */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); @@ -1486,6 +1530,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", + ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel); if (sel & WPA_CIPHER_AES_128_CMAC) { wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " @@ -1511,13 +1558,22 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, wpas_get_ssid_pmf(wpa_s, ssid)); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OCV + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv); +#endif /* CONFIG_OCV */ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); return -1; } - if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { + if (0) { +#ifdef CONFIG_DPP + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { + /* Use PMK from DPP network introduction (PMKSA entry) */ + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); +#endif /* CONFIG_DPP */ + } else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { int psk_set = 0; int sae_only; @@ -1901,6 +1957,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, if (wpa_s->current_bss && wpa_s->current_bss == bss) { wmm_ac_save_tspecs(wpa_s); wpa_s->reassoc_same_bss = 1; + } else if (wpa_s->current_bss && wpa_s->current_bss != bss) { + os_get_reltime(&wpa_s->roam_start); } } @@ -2169,9 +2227,14 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; + freq->channel = pri_chan->chan; + #ifdef CONFIG_HT_OVERRIDES - if (ssid->disable_ht40) - return; + if (ssid->disable_ht40) { + if (ssid->disable_vht) + return; + goto skip_ht40; + } #endif /* CONFIG_HT_OVERRIDES */ /* Check/setup HT40+/HT40- */ @@ -2196,8 +2259,6 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; - freq->channel = pri_chan->chan; - if (ht40 == -1) { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) return; @@ -2241,6 +2302,9 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, wpa_scan_results_free(scan_res); } +#ifdef CONFIG_HT_OVERRIDES +skip_ht40: +#endif /* CONFIG_HT_OVERRIDES */ wpa_printf(MSG_DEBUG, "IBSS/mesh: setup freq channel %d, sec_channel_offset %d", freq->channel, freq->sec_channel_offset); @@ -2330,6 +2394,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; seg0 = 114; } + } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_USE_HT) { + chwidth = VHT_CHANWIDTH_USE_HT; + seg0 = vht80[j] + 2; +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht40) + seg0 = 0; +#endif /* CONFIG_HT_OVERRIDES */ } if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, @@ -2450,6 +2521,9 @@ static u8 * wpas_populate_assoc_ies( #ifdef CONFIG_MBO const u8 *mbo_ie; #endif +#ifdef CONFIG_SAE + int sae_pmksa_cached = 0; +#endif /* CONFIG_SAE */ #ifdef CONFIG_FILS const u8 *realm, *username, *rrk; size_t realm_len, username_len, rrk_len; @@ -2487,8 +2561,12 @@ static u8 * wpas_populate_assoc_ies( #endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, try_opportunistic, - cache_id, 0) == 0) + cache_id, 0) == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); +#ifdef CONFIG_SAE + sae_pmksa_cached = 1; +#endif /* CONFIG_SAE */ + } wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { @@ -2601,6 +2679,14 @@ static u8 * wpas_populate_assoc_ies( "Overriding auth_alg selection: 0x%x", algs); } +#ifdef CONFIG_SAE + if (sae_pmksa_cached && algs == WPA_AUTH_ALG_SAE) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt"); + algs = WPA_AUTH_ALG_OPEN; + } +#endif /* CONFIG_SAE */ + #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; @@ -2633,7 +2719,7 @@ static u8 * wpas_populate_assoc_ies( #endif /* CONFIG_P2P */ if (bss) { - wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq, + wpa_ie_len += wpas_supp_op_class_ie(wpa_s, ssid, bss->freq, wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len); @@ -2678,7 +2764,8 @@ static u8 * wpas_populate_assoc_ies( int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); size_t len; - wpas_hs20_add_indication(hs20, pps_mo_id); + wpas_hs20_add_indication(hs20, pps_mo_id, + get_hs20_version(bss)); wpas_hs20_add_roam_cons_sel(hs20, ssid); len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(hs20) <= len) { @@ -2774,11 +2861,33 @@ static u8 * wpas_populate_assoc_ies( os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(owe_ie), wpabuf_len(owe_ie)); wpa_ie_len += wpabuf_len(owe_ie); - wpabuf_free(owe_ie); } + wpabuf_free(owe_ie); } #endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP2 + if (wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP && + ssid->dpp_netaccesskey) { + dpp_pfs_free(wpa_s->dpp_pfs); + wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len); + if (!wpa_s->dpp_pfs) { + wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS"); + /* Try to continue without PFS */ + goto pfs_fail; + } + if (wpabuf_len(wpa_s->dpp_pfs->ie) <= + max_wpa_ie_len - wpa_ie_len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(wpa_s->dpp_pfs->ie), + wpabuf_len(wpa_s->dpp_pfs->ie)); + wpa_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie); + } + } +pfs_fail: +#endif /* CONFIG_DPP2 */ + #ifdef CONFIG_IEEE80211R /* * Add MDIE under these conditions: the network profile allows FT, @@ -2812,6 +2921,21 @@ static u8 * wpas_populate_assoc_ies( } #endif /* CONFIG_IEEE80211R */ + if (ssid->multi_ap_backhaul_sta) { + size_t multi_ap_ie_len; + + multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len, + max_wpa_ie_len - wpa_ie_len, + MULTI_AP_BACKHAUL_STA); + if (multi_ap_ie_len == 0) { + wpa_printf(MSG_ERROR, + "Multi-AP: Failed to build Multi-AP IE"); + os_free(wpa_ie); + return NULL; + } + wpa_ie_len += multi_ap_ie_len; + } + params->wpa_ie = wpa_ie; params->wpa_ie_len = wpa_ie_len; params->auth_alg = algs; @@ -2850,6 +2974,34 @@ static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ +#ifdef CONFIG_MBO +void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_associate_params params; + u8 *wpa_ie; + + /* + * Update MBO connect params only in case of change of MBO attributes + * when connected, if the AP support MBO. + */ + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid || + !wpa_s->current_bss || + !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) + return; + + os_memset(¶ms, 0, sizeof(params)); + wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, ¶ms, NULL); + if (!wpa_ie) + return; + + wpa_drv_update_connect_params(wpa_s, ¶ms, WPA_DRV_UPDATE_ASSOC_IES); + os_free(wpa_ie); +} +#endif /* CONFIG_MBO */ + + static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) { struct wpa_connect_work *cwork = work->ctx; @@ -3055,7 +3207,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } params.wep_tx_keyidx = ssid->wep_tx_keyidx; - if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && (params.key_mgmt_suite == WPA_KEY_MGMT_PSK || params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { params.passphrase = ssid->passphrase; @@ -3063,6 +3215,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.psk = ssid->psk; } + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) && + (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) + params.req_key_mgmt_offload = 1; + if (wpa_s->conf->key_mgmt_offload) { if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || @@ -3290,6 +3449,9 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, zero_addr = 1; } + if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0) + wpa_s->enabled_4addr_mode = 0; + #ifdef CONFIG_TDLS wpa_tdls_teardown_peers(wpa_s->wpa); #endif /* CONFIG_TDLS */ @@ -3881,7 +4043,9 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) while (entry) { if (!wpas_network_disabled(wpa_s, entry) && ((ssid_len == entry->ssid_len && - os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) && + (!entry->ssid || + os_memcmp(ssid, entry->ssid, ssid_len) == 0)) || + wired) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; @@ -4059,7 +4223,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, } if (wpa_s->eapol_received == 0 && - (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) || + (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) || !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || wpa_s->wpa_state != WPA_COMPLETED) && (wpa_s->current_ssid == NULL || @@ -4125,7 +4289,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0) return; wpa_drv_poll(wpa_s); - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len); else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { /* @@ -4367,11 +4531,11 @@ static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s, { le16 msk; - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); - if (disabled == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); + msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE); htcaps_mask->ht_capabilities_info |= msk; if (disabled) @@ -4388,11 +4552,11 @@ static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int factor) { - wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); - if (factor == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); + if (factor < 0 || factor > 3) { wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. " "Must be 0-3 or -1", factor); @@ -4412,11 +4576,11 @@ static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int density) { - wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); - if (density == -1) return 0; + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); + if (density < 0 || density > 7) { wpa_msg(wpa_s, MSG_ERROR, "ampdu_density: %d out of range. Must be 0-7 or -1.", @@ -4437,7 +4601,8 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); + if (disabled) + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); set_disable_ht40(htcaps, disabled); set_disable_ht40(htcaps_mask, 0); @@ -4455,7 +4620,8 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | HT_CAP_INFO_SHORT_GI40MHZ); - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); + if (disabled) + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); if (disabled) htcaps->ht_capabilities_info &= ~msk; @@ -4476,7 +4642,8 @@ static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s, /* Masking these out disables LDPC */ le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP); - wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled); + if (disabled) + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled); if (disabled) htcaps->ht_capabilities_info &= ~msk; @@ -4489,6 +4656,58 @@ static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s, } +static int wpa_set_tx_stbc(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int tx_stbc) +{ + le16 msk = host_to_le16(HT_CAP_INFO_TX_STBC); + + if (tx_stbc == -1) + return 0; + + wpa_msg(wpa_s, MSG_DEBUG, "set_tx_stbc: %d", tx_stbc); + + if (tx_stbc < 0 || tx_stbc > 1) { + wpa_msg(wpa_s, MSG_ERROR, + "tx_stbc: %d out of range. Must be 0-1 or -1", tx_stbc); + return -EINVAL; + } + + htcaps_mask->ht_capabilities_info |= msk; + htcaps->ht_capabilities_info &= ~msk; + htcaps->ht_capabilities_info |= (tx_stbc << 7) & msk; + + return 0; +} + + +static int wpa_set_rx_stbc(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int rx_stbc) +{ + le16 msk = host_to_le16(HT_CAP_INFO_RX_STBC_MASK); + + if (rx_stbc == -1) + return 0; + + wpa_msg(wpa_s, MSG_DEBUG, "set_rx_stbc: %d", rx_stbc); + + if (rx_stbc < 0 || rx_stbc > 3) { + wpa_msg(wpa_s, MSG_ERROR, + "rx_stbc: %d out of range. Must be 0-3 or -1", rx_stbc); + return -EINVAL; + } + + htcaps_mask->ht_capabilities_info |= msk; + htcaps->ht_capabilities_info &= ~msk; + htcaps->ht_capabilities_info |= (rx_stbc << 8) & msk; + + return 0; +} + + void wpa_supplicant_apply_ht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params) @@ -4513,6 +4732,8 @@ void wpa_supplicant_apply_ht_overrides( wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40); wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi); wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc); + wpa_set_rx_stbc(wpa_s, htcaps, htcaps_mask, ssid->rx_stbc); + wpa_set_tx_stbc(wpa_s, htcaps, htcaps_mask, ssid->tx_stbc); if (ssid->ht40_intolerant) { le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT); @@ -4547,6 +4768,16 @@ void wpa_supplicant_apply_vht_overrides( vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask); #ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_sgi) { + vhtcaps_mask->vht_capabilities_info |= (VHT_CAP_SHORT_GI_80 | + VHT_CAP_SHORT_GI_160); + vhtcaps->vht_capabilities_info &= ~(VHT_CAP_SHORT_GI_80 | + VHT_CAP_SHORT_GI_160); + wpa_msg(wpa_s, MSG_DEBUG, + "disable-sgi override specified, vht-caps: 0x%x", + vhtcaps->vht_capabilities_info); + } + /* if max ampdu is <= 3, we have to make the HT cap the same */ if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) { int max_ampdu; @@ -5560,6 +5791,12 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, capa.mac_addr_rand_sched_scan_supported) wpa_s->mac_addr_rand_supported |= (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO); + + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + if (wpa_s->extended_capa && + wpa_s->extended_capa_len >= 3 && + wpa_s->extended_capa[2] & 0x40) + wpa_s->multi_bss_support = 1; } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; @@ -6543,6 +6780,9 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) * TODO: if more than one possible AP is available in scan results, * could try the other ones before requesting a new scan. */ + + /* speed up the connection attempt with normal scan */ + wpa_s->normal_scans = 0; wpa_supplicant_req_scan(wpa_s, timeout / 1000, 1000 * (timeout % 1000)); } @@ -6928,6 +7168,8 @@ void wpas_request_disconnection(struct wpa_supplicant *wpa_s) wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + radio_remove_works(wpa_s, "connect", 0); + radio_remove_works(wpa_s, "sme-connect", 0); } @@ -7182,16 +7424,14 @@ static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx) void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, - unsigned int sec) + unsigned int sec, int rssi_threshold) { struct wpa_bss_tmp_disallowed *bss; bss = wpas_get_disallowed_bss(wpa_s, bssid); if (bss) { eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); - eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, - wpa_s, bss); - return; + goto finish; } bss = os_malloc(sizeof(*bss)); @@ -7204,23 +7444,31 @@ void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, os_memcpy(bss->bssid, bssid, ETH_ALEN); dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list); wpa_set_driver_tmp_disallow_list(wpa_s); + +finish: + bss->rssi_threshold = rssi_threshold; eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, wpa_s, bss); } -int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) { - struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev; + struct wpa_bss_tmp_disallowed *disallowed = NULL, *tmp, *prev; dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { - if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) { - bss = tmp; + if (os_memcmp(bss->bssid, tmp->bssid, ETH_ALEN) == 0) { + disallowed = tmp; break; } } - if (!bss) + if (!disallowed) + return 0; + + if (disallowed->rssi_threshold != 0 && + bss->level > disallowed->rssi_threshold) return 0; return 1; diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 4f5916025aab..a9205f0b8a2c 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -282,6 +282,14 @@ fast_reauth=1 # to external program(s) #wps_cred_processing=0 +# Whether to enable SAE (WPA3-Personal transition mode) automatically for +# WPA2-PSK credentials received using WPS. +# 0 = only add the explicitly listed WPA2-PSK configuration (default) +# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the +# station gets configured in WPA3-Personal transition mode (supports both +# WPA2-Personal (PSK) and WPA3-Personal (SAE) APs). +#wps_cred_add_sae=0 + # Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing # The vendor attribute contents to be added in M1 (hex string) #wps_vendor_ext_m1=000137100100020001 @@ -310,6 +318,15 @@ fast_reauth=1 # of APs when using ap_scan=1 mode. #bss_max_count=200 +# BSS expiration age in seconds. A BSS will be removed from the local cache +# if it is not in use and has not been seen for this time. Default is 180. +#bss_expiration_age=180 + +# BSS expiration after number of scans. A BSS will be removed from the local +# cache if it is not seen in this number of scans. +# Default is 2. +#bss_expiration_scan_count=2 + # Automatic scan # This is an optional set of parameters for automatic scanning # within an interface in following format: @@ -377,11 +394,16 @@ fast_reauth=1 # Enabled SAE finite cyclic groups in preference order # By default (if this parameter is not set), the mandatory group 19 (ECC group -# defined over a 256-bit prime order field) is preferred, but other groups are -# also enabled. If this parameter is set, the groups will be tried in the -# indicated order. The group values are listed in the IANA registry: +# defined over a 256-bit prime order field, NIST P-256) is preferred and groups +# 20 (NIST P-384) and 21 (NIST P-521) are also enabled. If this parameter is +# set, the groups will be tried in the indicated order. +# The group values are listed in the IANA registry: # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 -#sae_groups=21 20 19 26 25 +# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production +# purposes due limited security (see RFC 8247). Groups that are not as strong as +# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases +# since all implementations are required to support group 19. +#sae_groups=19 20 21 # Default value for DTIM period (if not overridden in network block) #dtim_period=2 @@ -907,6 +929,13 @@ fast_reauth=1 # PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256 # (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used) # +# ocv: whether operating channel validation is enabled +# This is a countermeasure against multi-channel man-in-the-middle attacks. +# Enabling this automatically also enables ieee80211w, if not yet enabled. +# 0 = disabled (default) +# 1 = enabled +#ocv=1 +# # auth_alg: list of allowed IEEE 802.11 authentication algorithms # OPEN = Open System authentication (required for WPA/WPA2) # SHARED = Shared Key authentication (requires static WEP keys) @@ -987,6 +1016,22 @@ fast_reauth=1 # 0: Encrypt traffic (default) # 1: Integrity only # +# macsec_replay_protect: IEEE 802.1X/MACsec replay protection +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0: Replay protection disabled (default) +# 1: Replay protection enabled +# +# macsec_replay_window: IEEE 802.1X/MACsec replay protection window +# This determines a window in which replay is tolerated, to allow receipt +# of frames that have been misordered by the network. +# This setting applies only when MACsec replay protection active, i.e., +# - macsec_replay_protect is enabled +# - the key server has decided to enable MACsec +# 0: No replay window, strict check (default) +# 1..2^32-1: number of packets that could be misordered +# # macsec_port: IEEE 802.1X/MACsec port # Port component of the SCI # Range: 1-65534 (default: 1) @@ -995,9 +1040,10 @@ fast_reauth=1 # This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair. # In this mode, instances of wpa_supplicant can act as MACsec peers. The peer # with lower priority will become the key server and start distributing SAKs. -# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-bytes (128 bit) -# hex-string (32 hex-digits) -# mka_ckn (CKN = CAK Name) takes a 32-bytes (256 bit) hex-string (64 hex-digits) +# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit) +# hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits) +# mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string +# (2..64 hex-digits) # mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being # default priority # @@ -1143,6 +1189,12 @@ fast_reauth=1 # certificate may include additional sub-level labels in addition to the # required labels. # +# More than one match string can be provided by using semicolons to +# separate the strings (e.g., example.org;example.com). When multiple +# strings are specified, a match with any one of the values is considered +# a sufficient match for the certificate, i.e., the conditions are ORed +# together. +# # For example, domain_suffix_match=example.com would match # test.example.com but would not match test-example.com. # domain_match: Constraint for server domain name @@ -1155,6 +1207,12 @@ fast_reauth=1 # 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". +# +# More than one match string can be provided by using semicolons to +# separate the strings (e.g., example.org;example.com). When multiple +# strings are specified, a match with any one of the values is considered +# a sufficient match for the certificate, i.e., the conditions are ORed +# together. # phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters # (string with field-value pairs, e.g., "peapver=0" or # "peapver=1 peaplabel=1") @@ -1216,12 +1274,19 @@ fast_reauth=1 # For EAP-FAST, this must be set to 0 (or left unconfigured for the # default value to be used automatically). # tls_disable_tlsv1_0=1 - disable use of TLSv1.0 +# tls_disable_tlsv1_0=0 - explicitly enable use of TLSv1.0 (this allows +# systemwide TLS policies to be overridden) # tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers # that have issues interoperating with updated TLS version) +# tls_disable_tlsv1_1=0 - explicitly enable use of TLSv1.1 (this allows +# systemwide TLS policies to be overridden) # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers # that have issues interoperating with updated TLS version) +# tls_disable_tlsv1_2=0 - explicitly enable use of TLSv1.2 (this allows +# systemwide TLS policies to be overridden) # tls_disable_tlsv1_3=1 - disable use of TLSv1.3 (a workaround for AAA servers # that have issues interoperating with updated TLS version) +# tls_disable_tlsv1_3=0 - enable TLSv1.3 (experimental - disabled by default) # tls_ext_cert_check=0 - No external server certificate validation (default) # tls_ext_cert_check=1 - External server certificate validation enabled; this # requires an external program doing validation of server certificate @@ -1381,6 +1446,20 @@ fast_reauth=1 # Treated as hint by the kernel. # -1 = Do not make any changes. # 0-3 = Set AMPDU density (aka factor) to specified value. +# +# tx_stbc: Allow overriding STBC support for TX streams +# Value: 0-1, see IEEE Std 802.11-2016, 9.4.2.56.2. +# -1 = Do not make any changes (default) +# 0 = Set if not supported +# 1 = Set if supported +# +# rx_stbc: Allow overriding STBC support for RX streams +# Value: 0-3, see IEEE Std 802.11-2016, 9.4.2.56.2. +# -1 = Do not make any changes (default) +# 0 = Set if not supported +# 1 = Set for support of one spatial stream +# 2 = Set for support of one and two spatial streams +# 3 = Set for support of one, two and three spatial streams # disable_vht: Whether VHT should be disabled. # 0 = VHT enabled (if AP supports it) @@ -1396,6 +1475,13 @@ fast_reauth=1 # 2: MCS 0-9 # 3: not supported +# multi_ap_backhaul_sta: Multi-AP backhaul STA functionality +# 0 = normal STA (default) +# 1 = backhaul STA +# A backhaul STA sends the Multi-AP IE, fails to associate if the AP does not +# support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be +# added to a bridge to allow forwarding frames over this backhaul link. + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 8b749f44e235..16e4db62aeef 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -451,10 +451,12 @@ struct icon_entry { struct wpa_bss_tmp_disallowed { struct dl_list list; u8 bssid[ETH_ALEN]; + int rssi_threshold; }; struct beacon_rep_data { u8 token; + u8 last_indication; struct wpa_driver_scan_params scan_params; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; @@ -492,15 +494,16 @@ struct wpa_supplicant { struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; + struct os_reltime roam_start; + struct os_reltime roam_time; + struct os_reltime session_start; + struct os_reltime session_length; unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; #ifdef CONFIG_MATCH_IFACE int matched; #endif /* CONFIG_MATCH_IFACE */ -#ifdef CONFIG_CTRL_IFACE_DBUS - char *dbus_path; -#endif /* CONFIG_CTRL_IFACE_DBUS */ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; char *dbus_groupobj_path; @@ -745,6 +748,10 @@ struct wpa_supplicant { unsigned int wnmsleep_used:1; unsigned int owe_transition_select:1; unsigned int owe_transition_search:1; + unsigned int connection_set:1; + unsigned int connection_ht:1; + unsigned int connection_vht:1; + unsigned int connection_he:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; @@ -814,6 +821,7 @@ struct wpa_supplicant { unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; unsigned int mesh_vht_enabled:1; + struct wpa_driver_mesh_join_params *mesh_params; #ifdef CONFIG_PMKSA_CACHE_EXTERNAL /* struct external_pmksa_cache::list */ struct dl_list mesh_external_pmksa_cache; @@ -911,6 +919,7 @@ struct wpa_supplicant { unsigned int p2p_pd_before_go_neg:1; unsigned int p2p_go_ht40:1; unsigned int p2p_go_vht:1; + unsigned int p2p_go_he:1; unsigned int user_initiated_pd:1; unsigned int p2p_go_group_formation_completed:1; unsigned int group_formation_reported:1; @@ -1018,6 +1027,10 @@ struct wpa_supplicant { /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; + /* WLAN_STATUS_* status codes from last received Authentication frame + * from the AP. */ + u16 auth_status_code; + /* WLAN_STATUS_* status codes from (Re)Association Response frame. */ u16 assoc_status_code; @@ -1059,6 +1072,7 @@ struct wpa_supplicant { struct neighbor_report *wnm_neighbor_report_elements; struct os_reltime wnm_cand_valid_until; u8 wnm_cand_from_bss[ETH_ALEN]; + enum bss_trans_mgmt_status_code bss_tm_status; struct wpabuf *coloc_intf_elems; u8 coloc_intf_dialog_token; u8 coloc_intf_auto_report; @@ -1193,9 +1207,7 @@ struct wpa_supplicant { int last_auth_timeout_sec; #ifdef CONFIG_DPP - struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ - struct dl_list dpp_configurator; /* struct dpp_configurator */ - int dpp_init_done; + struct dpp_global *dpp; struct dpp_authentication *dpp_auth; struct wpa_radio_work *dpp_listen_work; unsigned int dpp_pending_listen_freq; @@ -1222,6 +1234,9 @@ struct wpa_supplicant { unsigned int dpp_resp_wait_time; unsigned int dpp_resp_max_tries; unsigned int dpp_resp_retry_time; +#ifdef CONFIG_DPP2 + struct dpp_pfs *dpp_pfs; +#endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override; char *dpp_discovery_override; @@ -1234,6 +1249,8 @@ struct wpa_supplicant { unsigned int disable_fils:1; #endif /* CONFIG_FILS */ unsigned int ieee80211ac:1; + unsigned int enabled_4addr_mode:1; + unsigned int multi_bss_support:1; }; @@ -1369,6 +1386,8 @@ int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, int add_oce_capa); const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr); const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr); +const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len, + enum mbo_attr_id attr); int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, const char *non_pref_chan); void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie); @@ -1383,6 +1402,7 @@ struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, const u8 *data, size_t slen); +void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s); /* op_classes.c */ enum chan_allowed { @@ -1391,8 +1411,9 @@ enum chan_allowed { enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel, u8 bw); -size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, - size_t len); +size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + int freq, u8 *pos, size_t len); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response @@ -1425,6 +1446,8 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid); int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s, + struct channel_list_changed *info); /* eap_register.c */ int eap_register_methods(void); @@ -1477,8 +1500,10 @@ struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, u16 num_modes, enum hostapd_hw_mode mode); void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, - unsigned int sec); -int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid); + unsigned int sec, int rssi_threshold); +int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); +void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s); struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 4634ed7fc368..449e04acded8 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -296,7 +296,7 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, } if (result != EAPOL_SUPP_RESULT_SUCCESS || - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) return; if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) @@ -1183,6 +1183,15 @@ static void wpa_supplicant_fils_hlp_rx(void *ctx, const u8 *dst, const u8 *src, os_free(hex); } + +static int wpa_supplicant_channel_info(void *_wpa_s, + struct wpa_channel_info *ci) +{ + struct wpa_supplicant *wpa_s = _wpa_s; + + return wpa_drv_channel_info(wpa_s, ci); +} + #endif /* CONFIG_NO_WPA */ @@ -1233,6 +1242,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload; ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk; ctx->fils_hlp_rx = wpa_supplicant_fils_hlp_rx; + ctx->channel_info = wpa_supplicant_channel_info; wpa_s->wpa = wpa_sm_init(ctx); if (wpa_s->wpa == NULL) { diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c index d3d06b8ae231..41477d514d3f 100644 --- a/wpa_supplicant/wpas_kay.c +++ b/wpa_supplicant/wpas_kay.c @@ -92,6 +92,12 @@ static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa) } +static int wpas_set_receive_lowest_pn(void *wpa_s, struct receive_sa *sa) +{ + return wpa_drv_set_receive_lowest_pn(wpa_s, sa); +} + + static unsigned int conf_offset_val(enum confidentiality_offset co) { switch (co) { @@ -219,6 +225,7 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn; kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn; kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn; + kay_ctx->set_receive_lowest_pn = wpas_set_receive_lowest_pn; kay_ctx->create_receive_sc = wpas_create_receive_sc; kay_ctx->delete_receive_sc = wpas_delete_receive_sc; kay_ctx->create_receive_sa = wpas_create_receive_sa; @@ -232,7 +239,8 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa; kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa; - res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port, + res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_replay_protect, + ssid->macsec_replay_window, ssid->macsec_port, ssid->mka_priority, wpa_s->ifname, wpa_s->own_addr); /* ieee802_1x_kay_init() frees kay_ctx on failure */ @@ -349,8 +357,8 @@ void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, /* Derive CAK from MSK */ cak->len = DEFAULT_KEY_LEN; - if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr, - peer_addr, cak->key)) { + if (ieee802_1x_cak_aes_cmac(msk->key, msk->len, wpa_s->own_addr, + peer_addr, cak->key, cak->len)) { wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CAK failed"); goto fail; @@ -359,9 +367,8 @@ void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, /* Derive CKN from MSK */ ckn->len = DEFAULT_CKN_LEN; - if (ieee802_1x_ckn_128bits_aes_cmac(msk->key, wpa_s->own_addr, - peer_addr, sid, sid_len, - ckn->name)) { + if (ieee802_1x_ckn_aes_cmac(msk->key, msk->len, wpa_s->own_addr, + peer_addr, sid, sid_len, ckn->name)) { wpa_printf(MSG_ERROR, "IEEE 802.1X: Deriving CKN failed"); goto fail; @@ -411,10 +418,10 @@ void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s, if (wpa_s->kay->policy == DO_NOT_SECURE) goto dealloc; - cak->len = MACSEC_CAK_LEN; + cak->len = ssid->mka_cak_len; os_memcpy(cak->key, ssid->mka_cak, cak->len); - ckn->len = MACSEC_CKN_LEN; + ckn->len = ssid->mka_ckn_len; os_memcpy(ckn->name, ssid->mka_ckn, ckn->len); res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE); diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 1a2677b8eea4..057927410256 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -530,11 +530,18 @@ static int wpa_supplicant_wps_cred(void *ctx, case WPS_AUTH_WPA2PSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; + if (wpa_s->conf->wps_cred_add_sae && + cred->key_len != 2 * PMK_LEN) { + ssid->key_mgmt |= WPA_KEY_MGMT_SAE; +#ifdef CONFIG_IEEE80211W + ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; +#endif /* CONFIG_IEEE80211W */ + } ssid->proto = WPA_PROTO_RSN; break; } - if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) { + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { if (cred->key_len == 2 * PMK_LEN) { if (hexstr2bin((const char *) cred->key, ssid->psk, PMK_LEN)) { @@ -1137,9 +1144,10 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, - int p2p_group) + int p2p_group, int multi_ap_backhaul_sta) { struct wpa_ssid *ssid; + char phase1[32]; #ifdef CONFIG_AP if (wpa_s->ap_iface) { @@ -1177,10 +1185,14 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, } } #endif /* CONFIG_P2P */ - if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0) + os_snprintf(phase1, sizeof(phase1), "pbc=1%s", + multi_ap_backhaul_sta ? " multi_ap=1" : ""); + if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0) return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; + if (multi_ap_backhaul_sta) + ssid->multi_ap_backhaul_sta = 1; wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index c8fe47e37279..0fbc85174f94 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -30,7 +30,7 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s); int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s); enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid); int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, - int p2p_group); + int p2p_group, int multi_ap_backhaul_sta); int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id); void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);