aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2019-04-22 15:42:53 +0000
committerCy Schubert <cy@FreeBSD.org>2019-04-22 15:42:53 +0000
commit6e6d0eb51ef7b7487340bae7f20097ee5a57dbf4 (patch)
tree406310b03a08c8e00c863a82934ba6a8e33c6b2f
parent8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7 (diff)
downloadsrc-6e6d0eb51ef7b7487340bae7f20097ee5a57dbf4.tar.gz
src-6e6d0eb51ef7b7487340bae7f20097ee5a57dbf4.zip
Import wpa_supplicant/hostapd 2.8vendor/wpa/2.8
Notes
Notes: svn path=/vendor/wpa/dist/; revision=346563 svn path=/vendor/wpa/2.8/; revision=346564; tag=vendor/wpa/2.8
-rw-r--r--CONTRIBUTIONS2
-rw-r--r--COPYING2
-rw-r--r--README2
-rw-r--r--hostapd/Android.mk9
-rw-r--r--hostapd/ChangeLog55
-rw-r--r--hostapd/Makefile12
-rw-r--r--hostapd/README2
-rw-r--r--hostapd/README-MULTI-AP160
-rw-r--r--hostapd/android.config3
-rw-r--r--hostapd/config_file.c250
-rw-r--r--hostapd/ctrl_iface.c153
-rw-r--r--hostapd/defconfig10
-rw-r--r--hostapd/hostapd.conf195
-rw-r--r--hostapd/hostapd.wpa_psk6
-rw-r--r--hostapd/hostapd_cli.c31
-rw-r--r--hostapd/main.c11
-rwxr-xr-xhostapd/wps-ap-nfc.py62
-rw-r--r--hs20/client/.gitignore1
-rw-r--r--hs20/client/Makefile5
-rw-r--r--hs20/client/est.c13
-rw-r--r--hs20/client/osu_client.c8
-rw-r--r--src/ap/acs.c92
-rw-r--r--src/ap/ap_config.c124
-rw-r--r--src/ap/ap_config.h31
-rw-r--r--src/ap/ap_drv_ops.h18
-rw-r--r--src/ap/authsrv.c16
-rw-r--r--src/ap/beacon.c22
-rw-r--r--src/ap/ctrl_iface_ap.c14
-rw-r--r--src/ap/dfs.c15
-rw-r--r--src/ap/dhcp_snoop.c18
-rw-r--r--src/ap/dpp_hostapd.c756
-rw-r--r--src/ap/dpp_hostapd.h6
-rw-r--r--src/ap/drv_callbacks.c72
-rw-r--r--src/ap/eap_user_db.c12
-rw-r--r--src/ap/fils_hlp.c13
-rw-r--r--src/ap/hostapd.c174
-rw-r--r--src/ap/hostapd.h14
-rw-r--r--src/ap/hs20.c17
-rw-r--r--src/ap/hw_features.c44
-rw-r--r--src/ap/ieee802_11.c663
-rw-r--r--src/ap/ieee802_11.h15
-rw-r--r--src/ap/ieee802_11_auth.c14
-rw-r--r--src/ap/ieee802_11_he.c31
-rw-r--r--src/ap/ieee802_11_shared.c302
-rw-r--r--src/ap/ieee802_11_vht.c23
-rw-r--r--src/ap/ieee802_1x.c107
-rw-r--r--src/ap/neighbor_db.c123
-rw-r--r--src/ap/neighbor_db.h3
-rw-r--r--src/ap/rrm.c2
-rw-r--r--src/ap/sta_info.c65
-rw-r--r--src/ap/sta_info.h10
-rw-r--r--src/ap/vlan_full.c87
-rw-r--r--src/ap/vlan_init.c10
-rw-r--r--src/ap/wnm_ap.c90
-rw-r--r--src/ap/wpa_auth.c401
-rw-r--r--src/ap/wpa_auth.h25
-rw-r--r--src/ap/wpa_auth_ft.c155
-rw-r--r--src/ap/wpa_auth_glue.c94
-rw-r--r--src/ap/wpa_auth_i.h13
-rw-r--r--src/ap/wpa_auth_ie.c87
-rw-r--r--src/ap/wpa_auth_ie.h4
-rw-r--r--src/ap/wps_hostapd.c68
-rw-r--r--src/common/common_module_tests.c178
-rw-r--r--src/common/defs.h32
-rw-r--r--src/common/dpp.c1096
-rw-r--r--src/common/dpp.h72
-rw-r--r--src/common/hw_features_common.c91
-rw-r--r--src/common/hw_features_common.h5
-rw-r--r--src/common/ieee802_11_common.c247
-rw-r--r--src/common/ieee802_11_common.h70
-rw-r--r--src/common/ieee802_11_defs.h192
-rw-r--r--src/common/linux_bridge.h15
-rw-r--r--src/common/ocv.c172
-rw-r--r--src/common/ocv.h40
-rw-r--r--src/common/qca-vendor.h471
-rw-r--r--src/common/sae.c347
-rw-r--r--src/common/sae.h2
-rw-r--r--src/common/version.h2
-rw-r--r--src/common/wpa_common.c57
-rw-r--r--src/common/wpa_common.h15
-rw-r--r--src/common/wpa_ctrl.c23
-rw-r--r--src/crypto/Makefile2
-rw-r--r--src/crypto/aes-internal-enc.c4
-rw-r--r--src/crypto/crypto.h9
-rw-r--r--src/crypto/crypto_gnutls.c43
-rw-r--r--src/crypto/crypto_internal-modexp.c41
-rw-r--r--src/crypto/crypto_internal.c3
-rw-r--r--src/crypto/crypto_libtomcrypt.c5
-rw-r--r--src/crypto/crypto_linux.c3
-rw-r--r--src/crypto/crypto_nettle.c36
-rw-r--r--src/crypto/crypto_openssl.c122
-rw-r--r--src/crypto/crypto_wolfssl.c15
-rw-r--r--src/crypto/dh_groups.c1
-rw-r--r--src/crypto/md4-internal.c2
-rw-r--r--src/crypto/random.c72
-rw-r--r--src/crypto/sha1-tlsprf.c3
-rw-r--r--src/crypto/sha512-internal.c8
-rw-r--r--src/crypto/sha512.c104
-rw-r--r--src/crypto/tls.h35
-rw-r--r--src/crypto/tls_gnutls.c75
-rw-r--r--src/crypto/tls_internal.c46
-rw-r--r--src/crypto/tls_none.c5
-rw-r--r--src/crypto/tls_openssl.c524
-rw-r--r--src/crypto/tls_wolfssl.c46
-rw-r--r--src/drivers/driver.h121
-rw-r--r--src/drivers/driver_atheros.c3
-rw-r--r--src/drivers/driver_bsd.c2
-rw-r--r--src/drivers/driver_common.c22
-rw-r--r--src/drivers/driver_hostap.c12
-rw-r--r--src/drivers/driver_macsec_linux.c163
-rw-r--r--src/drivers/driver_nl80211.c282
-rw-r--r--src/drivers/driver_nl80211.h1
-rw-r--r--src/drivers/driver_nl80211_capa.c226
-rw-r--r--src/drivers/driver_nl80211_event.c102
-rw-r--r--src/drivers/driver_nl80211_scan.c20
-rw-r--r--src/drivers/driver_openbsd.c3
-rw-r--r--src/drivers/driver_roboswitch.c36
-rw-r--r--src/drivers/driver_wext.c13
-rw-r--r--src/drivers/drivers.mak72
-rw-r--r--src/drivers/drivers.mk42
-rw-r--r--src/drivers/linux_ioctl.c21
-rw-r--r--src/drivers/nl80211_copy.h602
-rw-r--r--src/eap_common/eap_eke_common.c2
-rw-r--r--src/eap_common/eap_pwd_common.c302
-rw-r--r--src/eap_common/eap_pwd_common.h6
-rw-r--r--src/eap_common/eap_sake_common.c75
-rw-r--r--src/eap_common/eap_sake_common.h8
-rw-r--r--src/eap_peer/eap_config.h99
-rw-r--r--src/eap_peer/eap_fast.c28
-rw-r--r--src/eap_peer/eap_mschapv2.c10
-rw-r--r--src/eap_peer/eap_peap.c91
-rw-r--r--src/eap_peer/eap_pwd.c93
-rw-r--r--src/eap_peer/eap_sake.c12
-rw-r--r--src/eap_peer/eap_tls.c1
-rw-r--r--src/eap_peer/eap_tls_common.c87
-rw-r--r--src/eap_peer/eap_tls_common.h3
-rw-r--r--src/eap_peer/eap_ttls.c48
-rw-r--r--src/eap_peer/eap_wsc.c3
-rw-r--r--src/eap_server/eap.h3
-rw-r--r--src/eap_server/eap_i.h1
-rw-r--r--src/eap_server/eap_server.c31
-rw-r--r--src/eap_server/eap_server_aka.c4
-rw-r--r--src/eap_server/eap_server_gpsk.c4
-rw-r--r--src/eap_server/eap_server_mschapv2.c10
-rw-r--r--src/eap_server/eap_server_pax.c65
-rw-r--r--src/eap_server/eap_server_peap.c58
-rw-r--r--src/eap_server/eap_server_pwd.c119
-rw-r--r--src/eap_server/eap_server_sake.c44
-rw-r--r--src/eap_server/eap_server_sim.c3
-rw-r--r--src/eap_server/eap_server_tls.c21
-rw-r--r--src/eap_server/eap_server_tls_common.c31
-rw-r--r--src/eap_server/eap_server_ttls.c6
-rw-r--r--src/eap_server/eap_tls_common.h3
-rw-r--r--src/eapol_supp/eapol_supp_sm.c13
-rw-r--r--src/fst/fst.h16
-rw-r--r--src/lib.rules5
-rw-r--r--src/p2p/p2p.c34
-rw-r--r--src/p2p/p2p.h15
-rw-r--r--src/p2p/p2p_build.c2
-rw-r--r--src/p2p/p2p_group.c3
-rw-r--r--src/p2p/p2p_i.h4
-rw-r--r--src/p2p/p2p_invitation.c2
-rw-r--r--src/p2p/p2p_utils.c23
-rw-r--r--src/pae/ieee802_1x_cp.c36
-rw-r--r--src/pae/ieee802_1x_cp.h1
-rw-r--r--src/pae/ieee802_1x_kay.c964
-rw-r--r--src/pae/ieee802_1x_kay.h8
-rw-r--r--src/pae/ieee802_1x_kay_i.h52
-rw-r--r--src/pae/ieee802_1x_key.c89
-rw-r--r--src/pae/ieee802_1x_key.h26
-rw-r--r--src/pae/ieee802_1x_secy_ops.c25
-rw-r--r--src/pae/ieee802_1x_secy_ops.h2
-rw-r--r--src/radius/radius_client.c86
-rw-r--r--src/radius/radius_server.c255
-rw-r--r--src/radius/radius_server.h1
-rw-r--r--src/rsn_supp/pmksa_cache.c3
-rw-r--r--src/rsn_supp/tdls.c2
-rw-r--r--src/rsn_supp/wpa.c283
-rw-r--r--src/rsn_supp/wpa.h12
-rw-r--r--src/rsn_supp/wpa_ft.c43
-rw-r--r--src/rsn_supp/wpa_i.h18
-rw-r--r--src/rsn_supp/wpa_ie.c13
-rw-r--r--src/rsn_supp/wpa_ie.h4
-rw-r--r--src/tls/asn1.c8
-rw-r--r--src/tls/bignum.c4
-rw-r--r--src/tls/tlsv1_client.c34
-rw-r--r--src/tls/tlsv1_client.h3
-rw-r--r--src/tls/tlsv1_client_read.c2
-rw-r--r--src/tls/tlsv1_client_write.c3
-rw-r--r--src/tls/tlsv1_server.c57
-rw-r--r--src/tls/tlsv1_server.h7
-rw-r--r--src/tls/tlsv1_server_i.h2
-rw-r--r--src/tls/tlsv1_server_read.c51
-rw-r--r--src/tls/tlsv1_server_write.c5
-rw-r--r--src/tls/x509v3.c2
-rw-r--r--src/utils/Makefile1
-rw-r--r--src/utils/base64.c5
-rw-r--r--src/utils/browser.c3
-rw-r--r--src/utils/common.c36
-rw-r--r--src/utils/common.h1
-rw-r--r--src/utils/const_time.h191
-rw-r--r--src/utils/eloop.c29
-rw-r--r--src/utils/http_curl.c35
-rw-r--r--src/utils/json.c7
-rw-r--r--src/utils/list.h6
-rw-r--r--src/utils/os_internal.c16
-rw-r--r--src/utils/os_none.c6
-rw-r--r--src/utils/os_unix.c12
-rw-r--r--src/utils/utils_module_tests.c290
-rw-r--r--src/utils/wpa_debug.c6
-rw-r--r--src/wps/wps.c8
-rw-r--r--src/wps/wps.h38
-rw-r--r--src/wps/wps_attr_build.c14
-rw-r--r--src/wps/wps_attr_parse.c11
-rw-r--r--src/wps/wps_attr_parse.h1
-rw-r--r--src/wps/wps_common.c16
-rw-r--r--src/wps/wps_defs.h3
-rw-r--r--src/wps/wps_dev_attr.c8
-rw-r--r--src/wps/wps_dev_attr.h1
-rw-r--r--src/wps/wps_enrollee.c14
-rw-r--r--src/wps/wps_er.c4
-rw-r--r--src/wps/wps_i.h5
-rw-r--r--src/wps/wps_registrar.c88
-rw-r--r--src/wps/wps_upnp.c2
-rw-r--r--src/wps/wps_validate.c2
-rw-r--r--wpa_supplicant/Android.mk48
-rw-r--r--wpa_supplicant/ChangeLog69
-rw-r--r--wpa_supplicant/Makefile68
-rw-r--r--wpa_supplicant/README4
-rw-r--r--wpa_supplicant/README-DPP195
-rw-r--r--wpa_supplicant/README-P2P6
-rw-r--r--wpa_supplicant/android.config16
-rw-r--r--wpa_supplicant/ap.c59
-rw-r--r--wpa_supplicant/bss.c9
-rw-r--r--wpa_supplicant/bss.h3
-rw-r--r--wpa_supplicant/config.c130
-rw-r--r--wpa_supplicant/config.h49
-rw-r--r--wpa_supplicant/config_file.c34
-rw-r--r--wpa_supplicant/config_ssid.h76
-rw-r--r--wpa_supplicant/config_winreg.c6
-rw-r--r--wpa_supplicant/ctrl_iface.c226
-rw-r--r--wpa_supplicant/ctrl_iface_unix.c12
-rw-r--r--wpa_supplicant/dbus/Makefile4
-rw-r--r--wpa_supplicant/dbus/dbus-wpa_supplicant.conf8
-rw-r--r--wpa_supplicant/dbus/dbus_common.c8
-rw-r--r--wpa_supplicant/dbus/dbus_new.c342
-rw-r--r--wpa_supplicant/dbus/dbus_new.h27
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers.c445
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers.h18
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers_p2p.c97
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers_p2p.h1
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers_wps.c10
-rw-r--r--wpa_supplicant/dbus/dbus_old.c745
-rw-r--r--wpa_supplicant/dbus/dbus_old.h142
-rw-r--r--wpa_supplicant/dbus/dbus_old_handlers.c1393
-rw-r--r--wpa_supplicant/dbus/dbus_old_handlers.h101
-rw-r--r--wpa_supplicant/dbus/dbus_old_handlers_wps.c152
-rw-r--r--wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in5
-rw-r--r--wpa_supplicant/defconfig77
-rw-r--r--wpa_supplicant/doc/docbook/eapol_test.84
-rw-r--r--wpa_supplicant/doc/docbook/eapol_test.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_background.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_background.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_cli.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_cli.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_gui.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_gui.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_passphrase.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_passphrase.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_priv.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_priv.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_supplicant.822
-rw-r--r--wpa_supplicant/doc/docbook/wpa_supplicant.conf.52
-rw-r--r--wpa_supplicant/doc/docbook/wpa_supplicant.sgml30
-rw-r--r--wpa_supplicant/dpp_supplicant.c829
-rw-r--r--wpa_supplicant/dpp_supplicant.h10
-rw-r--r--wpa_supplicant/driver_i.h24
-rw-r--r--wpa_supplicant/eapol_test.c3
-rwxr-xr-xwpa_supplicant/eapol_test.py2
-rw-r--r--wpa_supplicant/events.c210
-rwxr-xr-xwpa_supplicant/examples/dbus-listen-preq.py20
-rwxr-xr-xwpa_supplicant/examples/dpp-qrcode.py36
-rwxr-xr-xwpa_supplicant/examples/p2p-nfc.py168
-rw-r--r--wpa_supplicant/examples/p2p/p2p_connect.py70
-rw-r--r--wpa_supplicant/examples/p2p/p2p_disconnect.py30
-rw-r--r--wpa_supplicant/examples/p2p/p2p_find.py34
-rw-r--r--wpa_supplicant/examples/p2p/p2p_flush.py30
-rw-r--r--wpa_supplicant/examples/p2p/p2p_group_add.py50
-rw-r--r--wpa_supplicant/examples/p2p/p2p_invite.py44
-rw-r--r--wpa_supplicant/examples/p2p/p2p_listen.py32
-rw-r--r--wpa_supplicant/examples/p2p/p2p_stop_find.py32
-rwxr-xr-xwpa_supplicant/examples/wpas-dbus-new-getall.py29
-rwxr-xr-xwpa_supplicant/examples/wpas-dbus-new-signals.py34
-rwxr-xr-xwpa_supplicant/examples/wpas-dbus-new-wps.py16
-rwxr-xr-xwpa_supplicant/examples/wpas-dbus-new.py20
-rwxr-xr-xwpa_supplicant/examples/wpas-test.py91
-rwxr-xr-xwpa_supplicant/examples/wps-nfc.py124
-rw-r--r--wpa_supplicant/gas_query.c2
-rw-r--r--wpa_supplicant/gas_query.h1
-rw-r--r--wpa_supplicant/hs20_supplicant.c31
-rw-r--r--wpa_supplicant/hs20_supplicant.h4
-rw-r--r--wpa_supplicant/ibss_rsn.c6
-rw-r--r--wpa_supplicant/interworking.c7
-rw-r--r--wpa_supplicant/main.c12
-rw-r--r--wpa_supplicant/mbo.c36
-rw-r--r--wpa_supplicant/mesh.c237
-rw-r--r--wpa_supplicant/mesh_mpm.c75
-rw-r--r--wpa_supplicant/mesh_rsn.c17
-rw-r--r--wpa_supplicant/notify.c81
-rw-r--r--wpa_supplicant/notify.h5
-rw-r--r--wpa_supplicant/op_classes.c68
-rw-r--r--wpa_supplicant/p2p_supplicant.c217
-rw-r--r--wpa_supplicant/p2p_supplicant.h9
-rw-r--r--wpa_supplicant/rrm.c227
-rw-r--r--wpa_supplicant/scan.c16
-rw-r--r--wpa_supplicant/sme.c260
-rw-r--r--wpa_supplicant/sme.h5
-rw-r--r--wpa_supplicant/systemd/wpa_supplicant.service.in4
-rwxr-xr-xwpa_supplicant/utils/log2pcap.py2
-rw-r--r--wpa_supplicant/wmm_ac.c11
-rw-r--r--wpa_supplicant/wnm_sta.c88
-rw-r--r--wpa_supplicant/wpa_cli.c131
-rw-r--r--wpa_supplicant/wpa_supplicant.c370
-rw-r--r--wpa_supplicant/wpa_supplicant.conf100
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h45
-rw-r--r--wpa_supplicant/wpas_glue.c12
-rw-r--r--wpa_supplicant/wpas_kay.c23
-rw-r--r--wpa_supplicant/wps_supplicant.c18
-rw-r--r--wpa_supplicant/wps_supplicant.h2
329 files changed, 16724 insertions, 7275 deletions
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 <j@w1.fi> and contributors
+Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> 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 <j@w1.fi> and contributors
+Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> 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 <j@w1.fi> and contributors
+Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> 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 <j@w1.fi> and contributors
+Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> 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 @@ fail:
}
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 @@ fail:
}
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 @@ fail:
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 @@ fail:
}
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 <bss-iface>.<vlan-id> 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:
-#<password/credential>[|mac=<peer mac>][|id=<identifier>]
+#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>][|id=<identifier>]
# 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=<keyid_string>
+# An optional VLAN ID can be specified by prefixing the line with
+# vlanid=<VLAN ID>.
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 <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* 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 <j@w1.fi> and contributors";
+"Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> 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,
"<id> = Get DPP configurator's private key" },
+ { "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL,
+ "conf=<role> configurator=<id> = 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,
"<addr> = poll a STA to check connectivity with a QoS null frame" },
+ { "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
+ "<addr> [req_mode=] <measurement request hexdump> = 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 <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* 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 <j@w1.fi> "
+ "Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> "
"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 @@ summary_file = None
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 @@ class HandoverServer(nfc.handover.HandoverServer):
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 @@ class HandoverServer(nfc.handover.HandoverServer):
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 <openssl/asn1t.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
+#include <openssl/opensslv.h>
#ifdef OPENSSL_IS_BORINGSSL
#include <openssl/buf.h>
#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/<fqdn>, 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;
+ }
+ }
+
+ bw = num_chan_to_bw(n_chans);
- /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+ /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */
- wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
- n_chans == 1 ? 20 :
- n_chans == 2 ? 40 :
- 80);
+ 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, &params)) {
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 <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* 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 @@ fail:
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 @@ skip_wpa_check:
}
#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 @@ fail:
}
+#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 <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* 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,
- &center_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 @@ dfs_offload:
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;
-
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
- return 1;
+ chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+ if (!chan)
+ return 0;
- 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" : "");
- }
+ if ((primary && chan_pri_allowed(chan)) ||
+ (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
+ return 1;
- 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,22 +522,58 @@ 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(&params, 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, &params);
+}
+
+
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 @@ reply:
}
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 <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* 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,
+ &center_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 @@ skip_poll:
} 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;
-
- if (hapd->conf->vlan_bridge[0]) {
- os_snprintf(br_name, IFNAMSIZ, "%s%d",
- hapd->conf->vlan_bridge, vid);
+ int ret;
+
+ 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 <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* 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 @@ continue_processing:
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 @@ continue_processing:
&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 @@ pmk_r1_derived:
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 <j@w1.fi>
+ * Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi>
*
* 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 @@ fail:
}
-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 @@ fail:
}
+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 @@ fail:
}
+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 @@ fail:
}
+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 @@ skip_groups:
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 @@ fail:
}
+#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 @@ fail:
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 <openssl/x509.h>
#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 <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* 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;
-
- pos = ies;
- end = ies + ies_len;
- ie = NULL;
+ const struct element *elem, *found = 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;
+
+ return NULL;
+}
- while (end - ies > 1) {
- if (2 + ies[1] > end - ies)
- break;
- if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 &&
- ies[2] == ext)
- return ies;
+const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
+{
+ const struct element *elem;
- ies += 2 + ies[1];
+ 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 <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* 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 <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
* 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 */
};
@@ -2438,6 +2529,18 @@ enum qca_wlan_vendor_attr_dmg_rf_sector_type {
};
/**
+ * 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
*
* @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
@@ -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 =
@@ -4578,6 +4704,20 @@ enum qca_wlan_vendor_attr_flush_pending {
};
/**
+ * 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
* point in the application-driver interaction sequence it can be retrieved by
@@ -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,
};
/**
@@ -6223,6 +6474,80 @@ enum qca_vendor_attr_coex_config {
};
/**
+ * 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.
*
* @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
@@ -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);
-
- crypto_bignum_deinit(a, 0);
- crypto_bignum_deinit(b, 0);
+ if (!b)
+ goto fail;
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
- return -1;
- }
+ res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+ if (res < 0)
+ goto fail;
- /* if (PWE > 1) --> found */
- if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
- wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
- return 0;
- }
+ /* 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);
- 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,36 +546,45 @@ 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. */
+
+ /* 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);
+ }
- /*
- * 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);
- }
+ 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) {
- wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
res = -1;
goto fail;
}
@@ -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;
-
- if (sae->tmp->pwe_ffc == NULL) {
- sae->tmp->pwe_ffc = crypto_bignum_init();
- if (sae->tmp->pwe_ffc == NULL)
- return -1;
- }
+ 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;
+
+ 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 <sys/stat.h>
+#include <fcntl.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
@@ -133,6 +135,19 @@ try_again:
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 @@ try_again:
}
#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 @@ retry_send:
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);
@@ -703,14 +704,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()
* Returns: Length of the prime defining the group
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 @@ err:
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 @@ err:
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 @@ err:
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 @@ done:
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 <fcntl.h>
+#ifdef CONFIG_GETRANDOM
+#include <sys/random.h>
+#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;
- }
- res = read(fd, dummy_key + dummy_key_avail,
- sizeof(dummy_key) - dummy_key_avail);
+#ifdef CONFIG_GETRANDOM
+ res = getrandom(dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK);
if (res < 0) {
- wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
- "%s", strerror(errno));
- 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);
}
- 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 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "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 @@ fail:
}
-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 @@ ocsp_error:
}
+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 <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* 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 */
@@ -111,6 +121,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
*/
u8 max_tx_power;
@@ -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
/**
@@ -1943,6 +1971,26 @@ struct wpa_signal_info {
};
/**
+ * 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)
* @tail: Tail portion of Beacon frame (after 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;
};
/**
@@ -3378,6 +3428,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
* @authmode: 1=Open System, 2=Shared Key, 3=both
@@ -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
*/
@@ -3782,6 +3840,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
* @sc: secure channel
@@ -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)
@@ -670,6 +690,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
* @sa: secure association
@@ -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));