From 8d61b8d6351bd83e8d9d5bef090cb72512d4da51 Mon Sep 17 00:00:00 2001
From: cy
Date: Thu, 6 Dec 2018 05:04:28 +0000
Subject: [PATCH] Import wpa_supplicant/hostapd 2.7
---
CONTRIBUTIONS | 2 +-
COPYING | 2 +-
README | 2 +-
hostapd/Android.mk | 142 +-
hostapd/ChangeLog | 55 +
hostapd/Makefile | 244 +-
hostapd/README | 30 +-
hostapd/android.config | 17 +-
hostapd/config_file.c | 816 +-
hostapd/config_file.h | 5 +
hostapd/ctrl_iface.c | 1040 ++-
hostapd/defconfig | 38 +-
hostapd/hlr_auc_gw.c | 4 +-
hostapd/hostapd.android.rc | 3 +-
hostapd/hostapd.conf | 449 +-
hostapd/hostapd.eap_user_sqlite | 18 +-
hostapd/hostapd_cli.c | 530 +-
hostapd/main.c | 44 +-
hs20/client/est.c | 6 -
hs20/client/oma_dm_client.c | 6 +
hs20/client/osu_client.c | 256 +-
hs20/client/osu_client.h | 1 +
src/ap/Makefile | 9 +-
src/ap/acs.c | 48 +-
src/ap/acs.h | 5 +
src/ap/ap_config.c | 163 +-
src/ap/ap_config.h | 143 +-
src/ap/ap_drv_ops.c | 76 +-
src/ap/ap_drv_ops.h | 15 +-
src/ap/ap_mlme.c | 16 +-
src/ap/authsrv.c | 52 +-
src/ap/beacon.c | 96 +-
src/ap/beacon.h | 2 +-
src/ap/bss_load.c | 52 +-
src/ap/ctrl_iface_ap.c | 361 +-
src/ap/ctrl_iface_ap.h | 4 +
src/ap/dfs.c | 65 +-
src/ap/dfs.h | 5 +-
src/ap/dhcp_snoop.c | 39 +-
src/ap/dpp_hostapd.c | 2096 +++++
src/ap/dpp_hostapd.h | 43 +
src/ap/drv_callbacks.c | 457 +-
src/ap/eap_user_db.c | 2 +
src/ap/eth_p_oui.c | 191 +
src/ap/eth_p_oui.h | 28 +
src/ap/fils_hlp.c | 641 ++
src/ap/fils_hlp.h | 27 +
src/ap/gas_query_ap.c | 714 ++
src/ap/gas_query_ap.h | 43 +
src/ap/gas_serv.c | 538 +-
src/ap/gas_serv.h | 10 +-
src/ap/hostapd.c | 400 +-
src/ap/hostapd.h | 94 +-
src/ap/hs20.c | 71 +
src/ap/hs20.h | 4 +
src/ap/hw_features.c | 127 +-
src/ap/ieee802_11.c | 1835 +++-
src/ap/ieee802_11.h | 31 +
src/ap/ieee802_11_auth.c | 15 +-
src/ap/ieee802_11_auth.h | 3 +-
src/ap/ieee802_11_he.c | 88 +
src/ap/ieee802_11_ht.c | 66 +-
src/ap/ieee802_11_shared.c | 200 +-
src/ap/ieee802_11_vht.c | 2 +-
src/ap/ieee802_1x.c | 212 +-
src/ap/ieee802_1x.h | 5 +
src/ap/ndisc_snoop.c | 1 +
src/ap/neighbor_db.c | 9 +-
src/ap/neighbor_db.h | 2 +-
src/ap/peerkey_auth.c | 396 -
src/ap/pmksa_cache_auth.c | 134 +-
src/ap/pmksa_cache_auth.h | 10 +
src/ap/rrm.c | 164 +-
src/ap/rrm.h | 5 +
src/ap/sta_info.c | 133 +-
src/ap/sta_info.h | 73 +-
src/ap/taxonomy.c | 1 +
src/ap/tkip_countermeasures.c | 5 +
src/ap/vlan_init.c | 14 +-
src/ap/wmm.c | 15 +-
src/ap/wnm_ap.c | 183 +-
src/ap/wnm_ap.h | 3 +
src/ap/wpa_auth.c | 1880 +++-
src/ap/wpa_auth.h | 283 +-
src/ap/wpa_auth_ft.c | 3575 +++++++-
src/ap/wpa_auth_glue.c | 712 +-
src/ap/wpa_auth_i.h | 104 +-
src/ap/wpa_auth_ie.c | 259 +-
src/ap/wpa_auth_ie.h | 14 +-
src/ap/wps_hostapd.c | 4 +-
src/common/common_module_tests.c | 55 +
src/common/ctrl_iface_common.c | 38 +-
src/common/ctrl_iface_common.h | 6 +-
src/common/defs.h | 70 +-
src/common/dhcp.h | 263 +
src/common/dpp.c | 7691 +++++++++++++++++
src/common/dpp.h | 435 +
src/common/gas.c | 2 +-
src/common/gas.h | 3 +
src/common/gas_server.c | 487 ++
src/common/gas_server.h | 44 +
src/common/hw_features_common.c | 106 +-
src/common/hw_features_common.h | 3 +
src/common/ieee802_11_common.c | 455 +-
src/common/ieee802_11_common.h | 50 +-
src/common/ieee802_11_defs.h | 520 +-
src/common/ieee802_1x_defs.h | 8 +
src/common/privsep_commands.h | 25 +-
src/common/qca-vendor.h | 4761 +++++++++-
src/common/sae.c | 243 +-
src/common/sae.h | 15 +-
src/common/version.h | 2 +-
src/common/wpa_common.c | 1064 ++-
src/common/wpa_common.h | 146 +-
src/common/wpa_ctrl.h | 91 +-
src/common/wpa_helpers.c | 3 +-
src/crypto/Makefile | 5 +
src/crypto/aes-ctr.c | 28 +-
src/crypto/aes-internal-dec.c | 4 +-
src/crypto/aes-internal-enc.c | 3 +-
src/crypto/aes-siv.c | 62 +-
src/crypto/aes.h | 4 +-
src/crypto/aes_siv.h | 10 +-
src/crypto/aes_wrap.h | 4 +-
src/crypto/crypto.h | 80 +-
src/crypto/crypto_gnutls.c | 216 +-
src/crypto/crypto_internal-modexp.c | 36 +
src/crypto/crypto_libtomcrypt.c | 49 +-
src/crypto/crypto_linux.c | 1006 +++
src/crypto/crypto_module_tests.c | 434 +-
src/crypto/crypto_nettle.c | 437 +
src/crypto/crypto_none.c | 3 +-
src/crypto/crypto_openssl.c | 394 +-
src/crypto/crypto_wolfssl.c | 1791 ++++
src/crypto/des-internal.c | 17 +-
src/crypto/dh_groups.c | 39 +-
src/crypto/fips_prf_wolfssl.c | 87 +
src/crypto/ms_funcs.c | 45 +-
src/crypto/ms_funcs.h | 8 +-
src/crypto/random.c | 5 +-
src/crypto/sha1-internal.c | 8 +-
src/crypto/sha256-internal.c | 6 +-
src/crypto/sha256-kdf.c | 20 +-
src/crypto/sha384-kdf.c | 87 +
src/crypto/sha384-prf.c | 28 +-
src/crypto/sha384.c | 104 +
src/crypto/sha384.h | 15 +-
src/crypto/sha512-kdf.c | 87 +
src/crypto/sha512-prf.c | 108 +
src/crypto/sha512.h | 27 +
src/crypto/tls.h | 18 +
src/crypto/tls_gnutls.c | 157 +-
src/crypto/tls_internal.c | 8 +
src/crypto/tls_none.c | 7 +
src/crypto/tls_openssl.c | 651 +-
src/crypto/tls_wolfssl.c | 2175 +++++
src/drivers/driver.h | 925 +-
src/drivers/driver_atheros.c | 194 +-
src/drivers/driver_bsd.c | 2 +-
src/drivers/driver_common.c | 23 +-
src/drivers/driver_hostap.c | 11 +-
src/drivers/driver_macsec_linux.c | 1310 +++
src/drivers/driver_macsec_qca.c | 659 +-
src/drivers/driver_ndis.c | 16 +-
src/drivers/driver_nl80211.c | 1654 +++-
src/drivers/driver_nl80211.h | 28 +-
src/drivers/driver_nl80211_capa.c | 270 +-
src/drivers/driver_nl80211_event.c | 331 +-
src/drivers/driver_nl80211_monitor.c | 11 +-
src/drivers/driver_nl80211_scan.c | 456 +-
src/drivers/driver_privsep.c | 56 +-
src/drivers/driver_wext.c | 42 +-
src/drivers/driver_wired.c | 348 +-
src/drivers/driver_wired_common.c | 322 +
src/drivers/driver_wired_common.h | 34 +
src/drivers/drivers.c | 3 +
src/drivers/drivers.mak | 13 +
src/drivers/drivers.mk | 12 +
src/drivers/nl80211_copy.h | 1003 ++-
src/eap_common/eap_eke_common.c | 32 +-
src/eap_common/eap_fast_common.c | 2 +-
src/eap_common/eap_pwd_common.c | 339 +-
src/eap_common/eap_pwd_common.h | 19 +-
src/eap_common/eap_sim_common.c | 9 +-
src/eap_peer/eap.c | 368 +-
src/eap_peer/eap.h | 35 +
src/eap_peer/eap_aka.c | 123 +-
src/eap_peer/eap_config.h | 12 +
src/eap_peer/eap_eke.c | 12 +-
src/eap_peer/eap_fast.c | 16 +-
src/eap_peer/eap_fast_pac.c | 15 +-
src/eap_peer/eap_gpsk.c | 18 +-
src/eap_peer/eap_i.h | 13 +
src/eap_peer/eap_ikev2.c | 6 +-
src/eap_peer/eap_leap.c | 17 +-
src/eap_peer/eap_mschapv2.c | 18 +-
src/eap_peer/eap_pax.c | 3 +-
src/eap_peer/eap_peap.c | 12 +-
src/eap_peer/eap_proxy.h | 12 +-
src/eap_peer/eap_proxy_dummy.c | 23 +-
src/eap_peer/eap_psk.c | 12 +-
src/eap_peer/eap_pwd.c | 587 +-
src/eap_peer/eap_sake.c | 12 +-
src/eap_peer/eap_sim.c | 61 +-
src/eap_peer/eap_tls.c | 34 +-
src/eap_peer/eap_tls_common.c | 44 +
src/eap_peer/eap_tls_common.h | 5 +
src/eap_peer/eap_ttls.c | 48 +-
src/eap_peer/ikev2.c | 3 +-
src/eap_peer/tncc.c | 6 +-
src/eap_server/eap.h | 6 +
src/eap_server/eap_i.h | 2 +
src/eap_server/eap_server.c | 81 +-
src/eap_server/eap_server_aka.c | 6 +-
src/eap_server/eap_server_eke.c | 9 +-
src/eap_server/eap_server_fast.c | 9 +-
src/eap_server/eap_server_gpsk.c | 12 +-
src/eap_server/eap_server_gtc.c | 3 +-
src/eap_server/eap_server_ikev2.c | 5 +-
src/eap_server/eap_server_mschapv2.c | 5 +-
src/eap_server/eap_server_pax.c | 3 +-
src/eap_server/eap_server_psk.c | 12 +-
src/eap_server/eap_server_pwd.c | 429 +-
src/eap_server/eap_server_sake.c | 9 +-
src/eap_server/eap_server_sim.c | 6 +-
src/eap_server/eap_server_tls.c | 19 +-
src/eap_server/eap_server_tls_common.c | 25 +-
src/eap_server/eap_server_ttls.c | 21 +-
src/eap_server/eap_server_wsc.c | 2 +-
src/eap_server/eap_tls_common.h | 7 +-
src/eap_server/ikev2.c | 6 +-
src/eap_server/tncs.c | 6 +-
src/eapol_auth/eapol_auth_sm.c | 17 +-
src/eapol_auth/eapol_auth_sm.h | 1 +
src/eapol_supp/eapol_supp_sm.c | 113 +-
src/eapol_supp/eapol_supp_sm.h | 50 +-
src/fst/fst_ctrl_aux.h | 22 +-
src/fst/fst_ctrl_iface.c | 2 +-
src/fst/fst_group.c | 2 +-
src/fst/fst_iface.h | 2 +-
src/fst/fst_session.c | 2 -
src/l2_packet/l2_packet.h | 1 +
src/l2_packet/l2_packet_linux.c | 29 +
src/l2_packet/l2_packet_privsep.c | 4 +-
src/p2p/p2p.c | 86 +-
src/p2p/p2p.h | 3 +
src/p2p/p2p_go_neg.c | 114 +-
src/p2p/p2p_group.c | 2 +
src/p2p/p2p_i.h | 7 +
src/p2p/p2p_pd.c | 5 +-
src/p2p/p2p_sd.c | 8 +-
src/pae/ieee802_1x_cp.c | 4 +
src/pae/ieee802_1x_kay.c | 386 +-
src/pae/ieee802_1x_kay.h | 136 +-
src/pae/ieee802_1x_kay_i.h | 180 +-
src/pae/ieee802_1x_secy_ops.c | 161 +-
src/pae/ieee802_1x_secy_ops.h | 11 +-
src/radius/radius.c | 21 +-
src/radius/radius.h | 10 +
src/radius/radius_client.c | 4 +-
src/radius/radius_das.c | 217 +-
src/radius/radius_das.h | 5 +
src/radius/radius_server.c | 654 +-
src/radius/radius_server.h | 5 +
src/rsn_supp/Makefile | 2 -
src/rsn_supp/peerkey.c | 1155 ---
src/rsn_supp/peerkey.h | 82 -
src/rsn_supp/pmksa_cache.c | 135 +-
src/rsn_supp/pmksa_cache.h | 45 +-
src/rsn_supp/preauth.c | 11 +-
src/rsn_supp/tdls.c | 44 +-
src/rsn_supp/wpa.c | 1785 +++-
src/rsn_supp/wpa.h | 69 +-
src/rsn_supp/wpa_ft.c | 332 +-
src/rsn_supp/wpa_i.h | 69 +-
src/rsn_supp/wpa_ie.c | 68 +-
src/rsn_supp/wpa_ie.h | 10 -
src/tls/libtommath.c | 222 +-
src/tls/rsa.c | 2 +-
src/tls/tlsv1_client.c | 2 +-
src/tls/tlsv1_client_read.c | 9 +-
src/tls/tlsv1_common.c | 8 +-
src/tls/tlsv1_cred.c | 6 +-
src/tls/tlsv1_server.c | 2 +-
src/tls/x509v3.c | 15 +-
src/utils/Makefile | 1 +
src/utils/base64.c | 139 +-
src/utils/base64.h | 4 +
src/utils/browser-wpadebug.c | 7 +-
src/utils/common.c | 21 +
src/utils/common.h | 15 +
src/utils/crc32.c | 85 +
src/utils/crc32.h | 14 +
src/utils/eloop.h | 8 +-
src/utils/http_curl.c | 34 +-
src/utils/json.c | 569 ++
src/utils/json.h | 42 +
src/utils/os.h | 12 +
src/utils/os_none.c | 6 +
src/utils/os_unix.c | 15 +
src/utils/os_win32.c | 10 +
src/utils/trace.c | 37 +-
src/utils/utils_module_tests.c | 81 +
src/utils/uuid.c | 25 +
src/utils/uuid.h | 1 +
src/utils/wpa_debug.c | 28 +-
src/utils/wpa_debug.h | 3 +
src/utils/wpabuf.c | 6 +-
src/utils/xml-utils.c | 8 +-
src/wps/wps.c | 14 +-
src/wps/wps_common.c | 1 +
src/wps/wps_er.c | 9 +-
src/wps/wps_registrar.c | 11 +-
wpa_supplicant/Android.mk | 158 +-
wpa_supplicant/ChangeLog | 70 +
wpa_supplicant/Makefile | 283 +-
wpa_supplicant/README | 15 +-
wpa_supplicant/README-HS20 | 19 +-
wpa_supplicant/android.config | 81 +-
wpa_supplicant/ap.c | 207 +-
wpa_supplicant/ap.h | 21 +-
wpa_supplicant/autoscan.c | 9 +-
wpa_supplicant/bgscan.c | 2 -
wpa_supplicant/bgscan_learn.c | 3 -
wpa_supplicant/bgscan_simple.c | 10 +-
wpa_supplicant/bss.c | 64 +-
wpa_supplicant/bss.h | 8 +
wpa_supplicant/config.c | 393 +-
wpa_supplicant/config.h | 152 +-
wpa_supplicant/config_file.c | 184 +-
wpa_supplicant/config_ssid.h | 197 +-
wpa_supplicant/config_winreg.c | 12 +-
wpa_supplicant/ctrl_iface.c | 1391 ++-
wpa_supplicant/ctrl_iface_named_pipe.c | 6 +-
wpa_supplicant/ctrl_iface_udp.c | 4 +-
wpa_supplicant/ctrl_iface_unix.c | 2 +-
wpa_supplicant/dbus/dbus_new.c | 258 +-
wpa_supplicant/dbus/dbus_new.h | 42 +-
wpa_supplicant/dbus/dbus_new_handlers.c | 422 +-
wpa_supplicant/dbus/dbus_new_handlers.h | 28 +
wpa_supplicant/dbus/dbus_new_handlers_p2p.c | 82 +-
wpa_supplicant/dbus/dbus_new_handlers_wps.c | 354 +-
wpa_supplicant/defconfig | 65 +-
wpa_supplicant/doc/docbook/eapol_test.8 | 4 +-
wpa_supplicant/doc/docbook/eapol_test.sgml | 2 +-
wpa_supplicant/doc/docbook/wpa_background.8 | 4 +-
.../doc/docbook/wpa_background.sgml | 2 +-
wpa_supplicant/doc/docbook/wpa_cli.8 | 4 +-
wpa_supplicant/doc/docbook/wpa_cli.sgml | 2 +-
wpa_supplicant/doc/docbook/wpa_gui.8 | 4 +-
wpa_supplicant/doc/docbook/wpa_gui.sgml | 2 +-
wpa_supplicant/doc/docbook/wpa_passphrase.8 | 4 +-
.../doc/docbook/wpa_passphrase.sgml | 4 +-
wpa_supplicant/doc/docbook/wpa_priv.8 | 4 +-
wpa_supplicant/doc/docbook/wpa_priv.sgml | 2 +-
wpa_supplicant/doc/docbook/wpa_supplicant.8 | 4 +-
.../doc/docbook/wpa_supplicant.conf.5 | 2 +-
.../doc/docbook/wpa_supplicant.sgml | 6 +-
wpa_supplicant/dpp_supplicant.c | 2613 ++++++
wpa_supplicant/dpp_supplicant.h | 39 +
wpa_supplicant/driver_i.h | 210 +-
wpa_supplicant/events.c | 1048 ++-
wpa_supplicant/examples/dpp-qrcode.py | 130 +
wpa_supplicant/examples/wps-ap-cli | 6 +-
wpa_supplicant/gas_query.c | 126 +-
wpa_supplicant/gas_query.h | 4 +-
wpa_supplicant/hs20_supplicant.c | 114 +-
wpa_supplicant/hs20_supplicant.h | 3 +
wpa_supplicant/ibss_rsn.c | 47 +-
wpa_supplicant/interworking.c | 163 +-
wpa_supplicant/interworking.h | 2 +-
wpa_supplicant/mbo.c | 393 +-
wpa_supplicant/mesh.c | 54 +-
wpa_supplicant/mesh_mpm.c | 32 +-
wpa_supplicant/mesh_rsn.c | 67 +-
wpa_supplicant/notify.c | 56 +-
wpa_supplicant/notify.h | 12 +-
wpa_supplicant/offchannel.c | 5 +-
wpa_supplicant/op_classes.c | 325 +
wpa_supplicant/p2p_supplicant.c | 286 +-
wpa_supplicant/preauth_test.c | 15 +-
wpa_supplicant/rrm.c | 1460 ++++
wpa_supplicant/scan.c | 377 +-
wpa_supplicant/sme.c | 811 +-
wpa_supplicant/sme.h | 14 +
wpa_supplicant/wifi_display.c | 13 +
wpa_supplicant/wmm_ac.c | 5 +-
wpa_supplicant/wnm_sta.c | 534 +-
wpa_supplicant/wnm_sta.h | 18 +-
wpa_supplicant/wpa_cli.c | 463 +-
wpa_supplicant/wpa_passphrase.c | 8 +-
wpa_supplicant/wpa_priv.c | 265 +-
wpa_supplicant/wpa_supplicant.c | 1922 ++--
wpa_supplicant/wpa_supplicant.conf | 192 +-
wpa_supplicant/wpa_supplicant_i.h | 213 +-
wpa_supplicant/wpa_supplicant_template.conf | 1 +
wpa_supplicant/wpas_glue.c | 169 +-
wpa_supplicant/wpas_kay.c | 166 +-
wpa_supplicant/wpas_kay.h | 10 +
wpa_supplicant/wps_supplicant.c | 20 +-
400 files changed, 69999 insertions(+), 10511 deletions(-)
create mode 100644 src/ap/dpp_hostapd.c
create mode 100644 src/ap/dpp_hostapd.h
create mode 100644 src/ap/eth_p_oui.c
create mode 100644 src/ap/eth_p_oui.h
create mode 100644 src/ap/fils_hlp.c
create mode 100644 src/ap/fils_hlp.h
create mode 100644 src/ap/gas_query_ap.c
create mode 100644 src/ap/gas_query_ap.h
create mode 100644 src/ap/ieee802_11_he.c
delete mode 100644 src/ap/peerkey_auth.c
create mode 100644 src/common/dhcp.h
create mode 100644 src/common/dpp.c
create mode 100644 src/common/dpp.h
create mode 100644 src/common/gas_server.c
create mode 100644 src/common/gas_server.h
create mode 100644 src/crypto/crypto_linux.c
create mode 100644 src/crypto/crypto_nettle.c
create mode 100644 src/crypto/crypto_wolfssl.c
create mode 100644 src/crypto/fips_prf_wolfssl.c
create mode 100644 src/crypto/sha384-kdf.c
create mode 100644 src/crypto/sha384.c
create mode 100644 src/crypto/sha512-kdf.c
create mode 100644 src/crypto/sha512-prf.c
create mode 100644 src/crypto/sha512.h
create mode 100644 src/crypto/tls_wolfssl.c
create mode 100644 src/drivers/driver_macsec_linux.c
create mode 100644 src/drivers/driver_wired_common.c
create mode 100644 src/drivers/driver_wired_common.h
delete mode 100644 src/rsn_supp/peerkey.c
delete mode 100644 src/rsn_supp/peerkey.h
create mode 100644 src/utils/crc32.c
create mode 100644 src/utils/crc32.h
create mode 100644 src/utils/json.c
create mode 100644 src/utils/json.h
create mode 100644 wpa_supplicant/dpp_supplicant.c
create mode 100644 wpa_supplicant/dpp_supplicant.h
create mode 100755 wpa_supplicant/examples/dpp-qrcode.py
create mode 100644 wpa_supplicant/op_classes.c
create mode 100644 wpa_supplicant/rrm.c
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
index 76600bc87280..053e8ecda90f 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-2016, Jouni Malinen and contributors
+Copyright (c) 2002-2018, Jouni Malinen and contributors
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/COPYING b/COPYING
index 7efce0dee1a7..55815d4016a3 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2016, Jouni Malinen and contributors
+Copyright (c) 2002-2018, Jouni Malinen and contributors
All Rights Reserved.
diff --git a/README b/README
index 9685f586beb7..6586d72ea039 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2016, Jouni Malinen and contributors
+Copyright (c) 2002-2018, Jouni Malinen and contributors
All Rights Reserved.
These programs are licensed under the BSD license (the one with
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index ea3a39a9719f..322f6a63255c 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -38,6 +38,9 @@ endif
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\"
+# Use Android specific directory for hostapd_cli command completion history
+L_CFLAGS += -DCONFIG_HOSTAPD_CLI_HISTORY_DIR=\"/data/misc/wifi\"
+
# To force sizeof(enum) = 4
ifeq ($(TARGET_ARCH),arm)
L_CFLAGS += -mabi=aapcs-linux
@@ -212,11 +215,6 @@ L_CFLAGS += -DCONFIG_RSN_PREAUTH
CONFIG_L2_PACKET=y
endif
-ifdef CONFIG_PEERKEY
-L_CFLAGS += -DCONFIG_PEERKEY
-OBJS += src/ap/peerkey_auth.c
-endif
-
ifdef CONFIG_HS20
NEED_AES_OMAC1=y
CONFIG_PROXYARP=y
@@ -244,11 +242,20 @@ NEED_AES_OMAC1=y
endif
ifdef CONFIG_IEEE80211R
-L_CFLAGS += -DCONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
OBJS += src/ap/wpa_auth_ft.c
NEED_SHA256=y
NEED_AES_OMAC1=y
NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef NEED_ETH_P_OUI
+L_CFLAGS += -DCONFIG_ETH_P_OUI
+OBJS += src/ap/eth_p_oui.c
endif
ifdef CONFIG_SAE
@@ -258,8 +265,30 @@ NEED_ECC=y
NEED_DH_GROUPS=y
endif
+ifdef CONFIG_OWE
+L_CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
+ifdef CONFIG_FILS
+L_CFLAGS += -DCONFIG_FILS
+OBJS += src/ap/fils_hlp.c
+NEED_SHA384=y
+NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+L_CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
ifdef CONFIG_WNM
-L_CFLAGS += -DCONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP
OBJS += src/ap/wnm_ap.c
endif
@@ -271,6 +300,10 @@ ifdef CONFIG_IEEE80211AC
L_CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_IEEE80211AX
+L_CFLAGS += -DCONFIG_IEEE80211AX
+endif
+
ifdef CONFIG_MBO
L_CFLAGS += -DCONFIG_MBO
OBJS += src/ap/mbo_ap.c
@@ -422,6 +455,7 @@ ifdef CONFIG_EAP_PWD
L_CFLAGS += -DEAP_SERVER_PWD
OBJS += src/eap_server/eap_server_pwd.c src/eap_common/eap_pwd_common.c
NEED_SHA256=y
+NEED_ECC=y
endif
ifdef CONFIG_EAP_EKE
@@ -499,6 +533,23 @@ endif
endif
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += src/ap/dpp_hostapd.c
+OBJS += src/ap/gas_query_ap.c
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+endif
+
ifdef CONFIG_EAP_IKEV2
L_CFLAGS += -DEAP_SERVER_IKEV2
OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c
@@ -581,25 +632,40 @@ NEED_SHA256=y
NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_h += -lcrypto
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
endif
ifeq ($(CONFIG_TLS), gnutls)
+ifndef CONFIG_CRYPTO
+# default to libgcrypt
+CONFIG_CRYPTO=gnutls
+endif
ifdef TLS_FUNCS
OBJS += src/crypto/tls_gnutls.c
LIBS += -lgnutls -lgpg-error
endif
-OBJS += src/crypto/crypto_gnutls.c
-HOBJS += src/crypto/crypto_gnutls.c
+OBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
+HOBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
ifdef NEED_FIPS186_2_PRF
OBJS += src/crypto/fips_prf_internal.c
OBJS += src/crypto/sha1-internal.c
endif
+ifeq ($(CONFIG_CRYPTO), gnutls)
LIBS += -lgcrypt
LIBS_h += -lgcrypt
-CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
+ifeq ($(CONFIG_CRYPTO), nettle)
+LIBS += -lnettle -lgmp
+LIBS_p += -lnettle -lgmp
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+endif
ifeq ($(CONFIG_TLS), internal)
ifndef CONFIG_CRYPTO
@@ -715,6 +781,12 @@ endif
ifdef NEED_AES_EAX
AESOBJS += src/crypto/aes-eax.c
NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
endif
ifdef NEED_AES_CTR
AESOBJS += src/crypto/aes-ctr.c
@@ -749,8 +821,10 @@ endif
SHA1OBJS =
ifdef NEED_SHA1
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), gnutls)
SHA1OBJS += src/crypto/sha1.c
endif
+endif
SHA1OBJS += src/crypto/sha1-prf.c
ifdef CONFIG_INTERNAL_SHA1
SHA1OBJS += src/crypto/sha1-internal.c
@@ -774,8 +848,10 @@ OBJS += $(SHA1OBJS)
endif
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), gnutls)
OBJS += src/crypto/md5.c
endif
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
@@ -811,8 +887,10 @@ endif
ifdef NEED_SHA256
L_CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), gnutls)
OBJS += src/crypto/sha256.c
endif
+endif
OBJS += src/crypto/sha256-prf.c
ifdef CONFIG_INTERNAL_SHA256
OBJS += src/crypto/sha256-internal.c
@@ -820,11 +898,36 @@ endif
ifdef NEED_TLS_PRF_SHA256
OBJS += src/crypto/sha256-tlsprf.c
endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += src/crypto/sha256-kdf.c
+endif
+ifdef NEED_HMAC_SHA384_KDF
+OBJS += src/crypto/sha384-kdf.c
+endif
+ifdef NEED_HMAC_SHA512_KDF
+OBJS += src/crypto/sha512-kdf.c
+endif
endif
ifdef NEED_SHA384
L_CFLAGS += -DCONFIG_SHA384
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), gnutls)
+OBJS += src/crypto/sha384.c
+endif
+endif
OBJS += src/crypto/sha384-prf.c
endif
+ifdef NEED_SHA512
+L_CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+OBJS += src/crypto/sha512.c
+endif
+endif
+endif
+OBJS += src/crypto/sha512-prf.c
+endif
ifdef CONFIG_INTERNAL_SHA384
L_CFLAGS += -DCONFIG_INTERNAL_SHA384
@@ -881,6 +984,11 @@ ifdef NEED_BASE64
OBJS += src/utils/base64.c
endif
+ifdef NEED_JSON
+OBJS += src/utils/json.c
+L_CFLAGS += -DCONFIG_JSON
+endif
+
ifdef NEED_AP_MLME
OBJS += src/ap/wmm.c
OBJS += src/ap/ap_list.c
@@ -897,6 +1005,10 @@ ifdef CONFIG_IEEE80211AC
OBJS += src/ap/ieee802_11_vht.c
endif
+ifdef CONFIG_IEEE80211AX
+OBJS += src/ap/ieee802_11_he.c
+endif
+
ifdef CONFIG_P2P_MANAGER
L_CFLAGS += -DCONFIG_P2P_MANAGER
OBJS += src/ap/p2p_hostapd.c
@@ -910,6 +1022,10 @@ endif
ifdef CONFIG_INTERWORKING
L_CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef NEED_GAS
OBJS += src/common/gas.c
OBJS += src/ap/gas_serv.c
endif
@@ -935,6 +1051,10 @@ ifdef CONFIG_NO_STDOUT_DEBUG
L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
endif
+ifdef CONFIG_DEBUG_SYSLOG
+L_CFLAGS += -DCONFIG_DEBUG_SYSLOG
+endif
+
ifdef CONFIG_DEBUG_LINUX_TRACING
L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
endif
@@ -968,6 +1088,7 @@ endif
include $(CLEAR_VARS)
LOCAL_MODULE := hostapd_cli
LOCAL_MODULE_TAGS := debug
+LOCAL_PROPRIETARY_MODULE := true
LOCAL_SHARED_LIBRARIES := libc libcutils liblog
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := $(OBJS_c)
@@ -978,6 +1099,7 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := hostapd
LOCAL_MODULE_TAGS := optional
+LOCAL_PROPRIETARY_MODULE := true
ifdef CONFIG_DRIVER_CUSTOM
LOCAL_STATIC_LIBRARIES := libCustomWifi
endif
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index d2b669b58654..f1366b4a9542 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,60 @@
ChangeLog for hostapd
+2018-12-02 - v2.7
+ * fixed WPA packet number reuse with replayed messages and key
+ reinstallation
+ [http://w1.fi/security/2017-1/] (CVE-2017-13082)
+ * added support for FILS (IEEE 802.11ai) shared key authentication
+ * added support for OWE (Opportunistic Wireless Encryption, RFC 8110;
+ and transition mode defined by WFA)
+ * added support for DPP (Wi-Fi Device Provisioning Protocol)
+ * FT:
+ - added local generation of PMK-R0/PMK-R1 for FT-PSK
+ (ft_psk_generate_local=1)
+ - replaced inter-AP protocol with a cleaner design that is more
+ easily extensible; this breaks backward compatibility and requires
+ all APs in the ESS to be updated at the same time to maintain FT
+ functionality
+ - added support for wildcard R0KH/R1KH
+ - replaced r0_key_lifetime (minutes) parameter with
+ ft_r0_key_lifetime (seconds)
+ - fixed wpa_psk_file use for FT-PSK
+ - fixed FT-SAE PMKID matching
+ - added expiration to PMK-R0 and PMK-R1 cache
+ - added IEEE VLAN support (including tagged VLANs)
+ - added support for SHA384 based AKM
+ * SAE
+ - fixed some PMKSA caching cases with SAE
+ - added support for configuring SAE password separately of the
+ WPA2 PSK/passphrase
+ - added option to require MFP for SAE associations
+ (sae_require_pmf=1)
+ - fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection
+ for SAE;
+ note: this is not backwards compatible, i.e., both the AP and
+ station side implementations will need to be update at the same
+ time to maintain interoperability
+ - added support for Password Identifier
+ * hostapd_cli: added support for command history and completion
+ * added support for requesting beacon report
+ * large number of other fixes, cleanup, and extensions
+ * added option to configure EAPOL-Key retry limits
+ (wpa_group_update_count and wpa_pairwise_update_count)
+ * removed all PeerKey functionality
+ * fixed nl80211 AP mode configuration regression with Linux 4.15 and
+ newer
+ * added support for using wolfSSL cryptographic library
+ * fixed some 20/40 MHz coexistence cases where the BSS could drop to
+ 20 MHz even when 40 MHz would be allowed
+ * Hotspot 2.0
+ - added support for setting Venue URL ANQP-element (venue_url)
+ - added support for advertising Hotspot 2.0 operator icons
+ - added support for Roaming Consortium Selection element
+ - added support for Terms and Conditions
+ - added support for OSEN connection in a shared RSN BSS
+ * added support for using OpenSSL 1.1.1
+ * added EAP-pwd server support for salted passwords
+
2016-10-02 - v2.6
* fixed EAP-pwd last fragment validation
[http://w1.fi/security/2015-7/] (CVE-2015-5314)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 46dffe5a3aca..2ce8b7ded842 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -258,11 +258,6 @@ CFLAGS += -DCONFIG_RSN_PREAUTH
CONFIG_L2_PACKET=y
endif
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
-
ifdef CONFIG_HS20
NEED_AES_OMAC1=y
CONFIG_PROXYARP=y
@@ -290,11 +285,20 @@ NEED_AES_OMAC1=y
endif
ifdef CONFIG_IEEE80211R
-CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
OBJS += ../src/ap/wpa_auth_ft.o
NEED_SHA256=y
NEED_AES_OMAC1=y
NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=y
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef NEED_ETH_P_OUI
+CFLAGS += -DCONFIG_ETH_P_OUI
+OBJS += ../src/ap/eth_p_oui.o
endif
ifdef CONFIG_SAE
@@ -305,8 +309,30 @@ NEED_DH_GROUPS=y
NEED_AP_MLME=y
endif
+ifdef CONFIG_OWE
+CFLAGS += -DCONFIG_OWE
+NEED_ECC=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+endif
+
+ifdef CONFIG_FILS
+CFLAGS += -DCONFIG_FILS
+OBJS += ../src/ap/fils_hlp.o
+NEED_SHA384=y
+NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
+endif
+
ifdef CONFIG_WNM
-CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP
OBJS += ../src/ap/wnm_ap.o
endif
@@ -318,6 +344,11 @@ ifdef CONFIG_IEEE80211AC
CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_IEEE80211AX
+CFLAGS += -DCONFIG_IEEE80211AX
+OBJS += ../src/ap/ieee802_11_he.o
+endif
+
ifdef CONFIG_MBO
CFLAGS += -DCONFIG_MBO
OBJS += ../src/ap/mbo_ap.o
@@ -458,6 +489,7 @@ ifdef CONFIG_EAP_PWD
CFLAGS += -DEAP_SERVER_PWD
OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o
NEED_SHA256=y
+NEED_ECC=y
endif
ifdef CONFIG_EAP_EKE
@@ -535,6 +567,23 @@ endif
endif
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += ../src/ap/dpp_hostapd.o
+OBJS += ../src/ap/gas_query_ap.o
+NEED_AES_SIV=y
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_HMAC_SHA512_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+NEED_SHA512=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+endif
+
ifdef CONFIG_EAP_IKEV2
CFLAGS += -DEAP_SERVER_IKEV2
OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o
@@ -602,7 +651,29 @@ CFLAGS += -DCONFIG_TLSV12
NEED_SHA256=y
endif
+ifeq ($(CONFIG_TLS), wolfssl)
+CONFIG_CRYPTO=wolfssl
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_wolfssl.o
+LIBS += -lwolfssl -lm
+endif
+OBJS += ../src/crypto/crypto_wolfssl.o
+HOBJS += ../src/crypto/crypto_wolfssl.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_wolfssl.o
+endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
+LIBS += -lwolfssl -lm
+LIBS_h += -lwolfssl -lm
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_h += -ldl
+endif
+endif
+
ifeq ($(CONFIG_TLS), openssl)
+CONFIG_CRYPTO=openssl
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_openssl.o
OBJS += ../src/crypto/tls_openssl_ocsp.o
@@ -617,29 +688,46 @@ NEED_SHA256=y
NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_h += -lcrypto
+LIBS_n += -lcrypto
ifdef CONFIG_TLS_ADD_DL
LIBS += -ldl
LIBS_h += -ldl
endif
+ifndef CONFIG_TLS_DEFAULT_CIPHERS
+CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
+endif
+CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
endif
ifeq ($(CONFIG_TLS), gnutls)
+ifndef CONFIG_CRYPTO
+# default to libgcrypt
+CONFIG_CRYPTO=gnutls
+endif
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_gnutls.o
LIBS += -lgnutls -lgpg-error
endif
-OBJS += ../src/crypto/crypto_gnutls.o
-HOBJS += ../src/crypto/crypto_gnutls.o
+OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
+HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_internal.o
SHA1OBJS += ../src/crypto/sha1-internal.o
endif
+ifeq ($(CONFIG_CRYPTO), gnutls)
LIBS += -lgcrypt
LIBS_h += -lgcrypt
-CONFIG_INTERNAL_SHA256=y
+LIBS_n += -lgcrypt
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
+ifeq ($(CONFIG_CRYPTO), nettle)
+LIBS += -lnettle -lgmp
+LIBS_p += -lnettle -lgmp
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+endif
ifeq ($(CONFIG_TLS), internal)
ifndef CONFIG_CRYPTO
@@ -720,6 +808,47 @@ CONFIG_INTERNAL_RC4=y
endif
endif
+ifeq ($(CONFIG_TLS), linux)
+OBJS += ../src/crypto/crypto_linux.o
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_server.o
+OBJS += ../src/tls/tlsv1_server_write.o
+OBJS += ../src/tls/tlsv1_server_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DLTM_FAST
+endif
+CONFIG_INTERNAL_DH_GROUP5=y
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_internal.o
+OBJS += ../src/crypto/sha1-internal.o
+endif
+endif
+
ifeq ($(CONFIG_TLS), none)
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_none.o
@@ -750,11 +879,19 @@ AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o
endif
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), wolfssl)
AESOBJS += ../src/crypto/aes-wrap.o
endif
+endif
ifdef NEED_AES_EAX
AESOBJS += ../src/crypto/aes-eax.o
NEED_AES_CTR=y
+NEED_AES_OMAC1=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+NEED_AES_CTR=y
+NEED_AES_OMAC1=y
endif
ifdef NEED_AES_CTR
AESOBJS += ../src/crypto/aes-ctr.o
@@ -763,20 +900,32 @@ ifdef NEED_AES_ENCBLOCK
AESOBJS += ../src/crypto/aes-encblock.o
endif
ifdef NEED_AES_OMAC1
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), wolfssl)
AESOBJS += ../src/crypto/aes-omac1.o
endif
+endif
+endif
ifdef NEED_AES_UNWRAP
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), wolfssl)
NEED_AES_DEC=y
AESOBJS += ../src/crypto/aes-unwrap.o
endif
endif
+endif
+endif
ifdef NEED_AES_CBC
NEED_AES_DEC=y
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), wolfssl)
AESOBJS += ../src/crypto/aes-cbc.o
endif
endif
+endif
+endif
ifdef NEED_AES_DEC
ifdef CONFIG_INTERNAL_AES
AESOBJS += ../src/crypto/aes-internal-dec.o
@@ -788,8 +937,14 @@ endif
ifdef NEED_SHA1
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
SHA1OBJS += ../src/crypto/sha1.o
endif
+endif
+endif
+endif
SHA1OBJS += ../src/crypto/sha1-prf.o
ifdef CONFIG_INTERNAL_SHA1
SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -798,8 +953,10 @@ SHA1OBJS += ../src/crypto/fips_prf_internal.o
endif
endif
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), wolfssl)
SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
endif
+endif
ifdef NEED_T_PRF
SHA1OBJS += ../src/crypto/sha1-tprf.o
endif
@@ -813,8 +970,14 @@ OBJS += $(SHA1OBJS)
endif
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
OBJS += ../src/crypto/md5.o
endif
+endif
+endif
+endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
@@ -830,6 +993,7 @@ endif
endif
ifdef NEED_DES
+CFLAGS += -DCONFIG_DES
ifdef CONFIG_INTERNAL_DES
OBJS += ../src/crypto/des-internal.o
endif
@@ -850,8 +1014,14 @@ endif
ifdef NEED_SHA256
CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
OBJS += ../src/crypto/sha256.o
endif
+endif
+endif
+endif
OBJS += ../src/crypto/sha256-prf.o
ifdef CONFIG_INTERNAL_SHA256
OBJS += ../src/crypto/sha256-internal.o
@@ -862,11 +1032,39 @@ endif
ifdef NEED_HMAC_SHA256_KDF
OBJS += ../src/crypto/sha256-kdf.o
endif
+ifdef NEED_HMAC_SHA384_KDF
+OBJS += ../src/crypto/sha384-kdf.o
+endif
+ifdef NEED_HMAC_SHA512_KDF
+OBJS += ../src/crypto/sha512-kdf.o
+endif
endif
ifdef NEED_SHA384
CFLAGS += -DCONFIG_SHA384
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
+OBJS += ../src/crypto/sha384.o
+endif
+endif
+endif
+endif
OBJS += ../src/crypto/sha384-prf.o
endif
+ifdef NEED_SHA512
+CFLAGS += -DCONFIG_SHA512
+ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), gnutls)
+ifneq ($(CONFIG_TLS), wolfssl)
+OBJS += ../src/crypto/sha512.o
+endif
+endif
+endif
+endif
+OBJS += ../src/crypto/sha512-prf.o
+endif
ifdef CONFIG_INTERNAL_SHA384
CFLAGS += -DCONFIG_INTERNAL_SHA384
@@ -902,9 +1100,13 @@ HOBJS += ../src/crypto/random.o
HOBJS += ../src/utils/eloop.o
HOBJS += $(SHA1OBJS)
ifneq ($(CONFIG_TLS), openssl)
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), wolfssl)
HOBJS += ../src/crypto/md5.o
endif
endif
+endif
+endif
ifdef CONFIG_RADIUS_SERVER
CFLAGS += -DRADIUS_SERVER
@@ -923,6 +1125,11 @@ ifdef NEED_BASE64
OBJS += ../src/utils/base64.o
endif
+ifdef NEED_JSON
+OBJS += ../src/utils/json.o
+CFLAGS += -DCONFIG_JSON
+endif
+
ifdef NEED_AP_MLME
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
@@ -952,6 +1159,10 @@ endif
ifdef CONFIG_INTERWORKING
CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef NEED_GAS
OBJS += ../src/common/gas.o
OBJS += ../src/ap/gas_serv.o
endif
@@ -983,6 +1194,10 @@ ifdef CONFIG_NO_STDOUT_DEBUG
CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
endif
+ifdef CONFIG_DEBUG_SYSLOG
+CFLAGS += -DCONFIG_DEBUG_SYSLOG
+endif
+
ifdef CONFIG_DEBUG_LINUX_TRACING
CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
endif
@@ -1082,16 +1297,14 @@ endif
ifdef CONFIG_INTERNAL_MD5
NOBJS += ../src/crypto/md5-internal.o
endif
-NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o
+NOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
+NOBJS += ../src/utils/os_$(CONFIG_OS).o
NOBJS += ../src/utils/wpa_debug.o
NOBJS += ../src/utils/wpabuf.o
ifdef CONFIG_WPA_TRACE
NOBJS += ../src/utils/trace.o
LIBS_n += -lbfd
endif
-ifdef TLS_FUNCS
-LIBS_n += -lcrypto
-endif
HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
HOBJS += ../src/crypto/aes-encblock.o
@@ -1099,6 +1312,9 @@ ifdef CONFIG_INTERNAL_AES
HOBJS += ../src/crypto/aes-internal.o
HOBJS += ../src/crypto/aes-internal-enc.o
endif
+ifeq ($(CONFIG_TLS), linux)
+HOBJS += ../src/crypto/crypto_linux.o
+endif
nt_password_hash: $(NOBJS)
$(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
diff --git a/hostapd/README b/hostapd/README
index 5d5fd365bb62..ae5317698ee8 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-2016, Jouni Malinen and contributors
+Copyright (c) 2002-2018, Jouni Malinen and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
@@ -70,7 +70,7 @@ Requirements
Current hardware/software requirements:
- drivers:
Host AP driver for Prism2/2.5/3.
- (http://hostap.epitest.fi/)
+ (http://w1.fi/hostap-driver.html)
Please note that station firmware version needs to be 1.7.0 or newer
to work in WPA mode.
@@ -81,8 +81,7 @@ Current hardware/software requirements:
Any wired Ethernet driver for wired IEEE 802.1X authentication
(experimental code)
- FreeBSD -current (with some kernel mods that have not yet been
- committed when hostapd v0.3.0 was released)
+ FreeBSD -current
BSD net80211 layer (e.g., Atheros driver)
@@ -186,23 +185,13 @@ Authenticator and RADIUS encapsulation between the Authenticator and
the Authentication Server. Other than this, the functionality is similar
to the case with the co-located Authentication Server.
-Authentication Server and Supplicant
-------------------------------------
+Authentication Server
+---------------------
Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
Authentication Server with hostapd Authenticator. FreeRADIUS
(http://www.freeradius.org/) has been successfully tested with hostapd
-Authenticator and both Xsupplicant (http://www.open1x.org) and Windows
-XP Supplicants. EAP/TLS was used with Xsupplicant and
-EAP/MD5-Challenge with Windows XP.
-
-http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information
-about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace
-Cisco access point with Host AP driver, hostapd daemon, and a Prism2
-card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information
-about using EAP/MD5 with FreeRADIUS, including instructions for WinXP
-configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on
-EAP/TLS use with WinXP Supplicant.
+Authenticator.
Automatic WEP key configuration
-------------------------------
@@ -243,16 +232,15 @@ networks that require some kind of security. Task group I (Security)
of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
to address the flaws of the base standard and has in practice
completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
-802.11 standard was approved in June 2004 and this amendment is likely
-to be published in July 2004.
+802.11 standard was approved in June 2004 and this amendment was
+published in July 2004.
Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
IEEE 802.11i work (draft 3.0) to define a subset of the security
enhancements that can be implemented with existing wlan hardware. This
is called Wi-Fi Protected Access (WPA). This has now become a
mandatory component of interoperability testing and certification done
-by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
-site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+by Wi-Fi Alliance.
IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
for protecting wireless networks. WEP uses RC4 with 40-bit keys,
diff --git a/hostapd/android.config b/hostapd/android.config
index e382c4081ebb..08d21f044aa7 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -44,9 +44,6 @@ CONFIG_DRIVER_NL80211_QCA=y
# WPA2/IEEE 802.11i RSN pre-authentication
#CONFIG_RSN_PREAUTH=y
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-#CONFIG_PEERKEY=y
-
# IEEE 802.11w (management frame protection)
# This version is an experimental implementation based on IEEE 802.11w/D1.0
# draft and is subject to change since the standard has not yet been finalized.
@@ -199,3 +196,17 @@ CONFIG_AP=y
# These extentions facilitate efficient use of multiple frequency bands
# available to the AP and the devices that may associate with it.
#CONFIG_MBO=y
+
+# Include internal line edit mode in hostapd_cli.
+CONFIG_WPA_CLI_EDIT=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
+# Wpa_supplicant's random pool is not necessary on Android. Randomness is
+# already provided by the entropymixer service which ensures sufficient
+# entropy is maintained across reboots. Commit b410eb1913 'Initialize
+# /dev/urandom earlier in boot' seeds /dev/urandom with that entropy before
+# either wpa_supplicant or hostapd are run.
+CONFIG_NO_RANDOM_POOL=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 5079f69e3bc5..b26da71a8410 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration file parser
- * Copyright (c) 2003-2015, Jouni Malinen
+ * Copyright (c) 2003-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,6 +14,8 @@
#include "utils/common.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
+#include "crypto/sha256.h"
+#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eap_server/eap.h"
#include "radius/radius_client.h"
@@ -111,7 +113,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
#endif /* CONFIG_NO_VLAN */
-static int hostapd_acl_comp(const void *a, const void *b)
+int hostapd_acl_comp(const void *a, const void *b)
{
const struct mac_acl_entry *aa = a;
const struct mac_acl_entry *bb = b;
@@ -119,6 +121,44 @@ static int hostapd_acl_comp(const void *a, const void *b)
}
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+ int vlan_id, const u8 *addr)
+{
+ struct mac_acl_entry *newacl;
+
+ newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+ if (!newacl) {
+ wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+ return -1;
+ }
+
+ *acl = newacl;
+ os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+ os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+ (*acl)[*num].vlan_id.untagged = vlan_id;
+ (*acl)[*num].vlan_id.notempty = !!vlan_id;
+ (*num)++;
+
+ return 0;
+}
+
+
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+ const u8 *addr)
+{
+ int i = 0;
+
+ while (i < *num) {
+ if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+ os_remove_in_array(*acl, *num, sizeof(**acl), i);
+ (*num)--;
+ } else {
+ i++;
+ }
+ }
+}
+
+
static int hostapd_config_read_maclist(const char *fname,
struct mac_acl_entry **acl, int *num)
{
@@ -126,12 +166,8 @@ static int hostapd_config_read_maclist(const char *fname,
char buf[128], *pos;
int line = 0;
u8 addr[ETH_ALEN];
- struct mac_acl_entry *newacl;
int vlan_id;
- if (!fname)
- return 0;
-
f = fopen(fname, "r");
if (!f) {
wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
@@ -139,7 +175,7 @@ static int hostapd_config_read_maclist(const char *fname,
}
while (fgets(buf, sizeof(buf), f)) {
- int i, rem = 0;
+ int rem = 0;
line++;
@@ -169,16 +205,7 @@ static int hostapd_config_read_maclist(const char *fname,
}
if (rem) {
- i = 0;
- while (i < *num) {
- if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) ==
- 0) {
- os_remove_in_array(*acl, *num,
- sizeof(**acl), i);
- (*num)--;
- } else
- i++;
- }
+ hostapd_remove_acl_mac(acl, num, addr);
continue;
}
vlan_id = 0;
@@ -190,31 +217,78 @@ static int hostapd_config_read_maclist(const char *fname,
if (*pos != '\0')
vlan_id = atoi(pos);
- newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
- if (newacl == NULL) {
- wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+ if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) {
fclose(f);
return -1;
}
-
- *acl = newacl;
- os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
- os_memset(&(*acl)[*num].vlan_id, 0,
- sizeof((*acl)[*num].vlan_id));
- (*acl)[*num].vlan_id.untagged = vlan_id;
- (*acl)[*num].vlan_id.notempty = !!vlan_id;
- (*num)++;
}
fclose(f);
- qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+ if (*acl)
+ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
return 0;
}
#ifdef EAP_SERVER
+
+static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user,
+ const char *hash, size_t len,
+ char **pos, int line,
+ const char *fname)
+{
+ char *pos2 = *pos;
+
+ while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#')
+ pos2++;
+
+ if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */
+ wpa_printf(MSG_ERROR,
+ "Invalid salted %s hash on line %d in '%s'",
+ hash, line, fname);
+ return -1;
+ }
+
+ user->password = os_malloc(len);
+ if (!user->password) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate memory for salted %s hash",
+ hash);
+ return -1;
+ }
+
+ if (hexstr2bin(*pos, user->password, len) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Invalid salted password on line %d in '%s'",
+ line, fname);
+ return -1;
+ }
+ user->password_len = len;
+ *pos += 2 * len;
+
+ user->salt_len = (pos2 - *pos) / 2;
+ user->salt = os_malloc(user->salt_len);
+ if (!user->salt) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate memory for salted %s hash",
+ hash);
+ return -1;
+ }
+
+ if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) {
+ wpa_printf(MSG_ERROR,
+ "Invalid salt for password on line %d in '%s'",
+ line, fname);
+ return -1;
+ }
+
+ *pos = pos2;
+ return 0;
+}
+
+
static int hostapd_config_read_eap_user(const char *fname,
struct hostapd_bss_config *conf)
{
@@ -223,9 +297,6 @@ static int hostapd_config_read_eap_user(const char *fname,
int line = 0, ret = 0, num_methods;
struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
- if (!fname)
- return 0;
-
if (os_strncmp(fname, "sqlite:", 7) == 0) {
#ifdef CONFIG_SQLITE
os_free(conf->eap_user_sqlite);
@@ -312,13 +383,12 @@ static int hostapd_config_read_eap_user(const char *fname,
goto failed;
}
- user->identity = os_malloc(pos - start);
+ user->identity = os_memdup(start, pos - start);
if (user->identity == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate "
"memory for EAP identity");
goto failed;
}
- os_memcpy(user->identity, start, pos - start);
user->identity_len = pos - start;
if (pos[0] == '"' && pos[1] == '*') {
@@ -436,13 +506,12 @@ static int hostapd_config_read_eap_user(const char *fname,
goto failed;
}
- user->password = os_malloc(pos - start);
+ user->password = os_memdup(start, pos - start);
if (user->password == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate "
"memory for EAP password");
goto failed;
}
- os_memcpy(user->password, start, pos - start);
user->password_len = pos - start;
pos++;
@@ -471,6 +540,24 @@ static int hostapd_config_read_eap_user(const char *fname,
user->password_len = 16;
user->password_hash = 1;
pos = pos2;
+ } else if (os_strncmp(pos, "ssha1:", 6) == 0) {
+ pos += 6;
+ if (hostapd_config_eap_user_salted(user, "sha1", 20,
+ &pos,
+ line, fname) < 0)
+ goto failed;
+ } else if (os_strncmp(pos, "ssha256:", 8) == 0) {
+ pos += 8;
+ if (hostapd_config_eap_user_salted(user, "sha256", 32,
+ &pos,
+ line, fname) < 0)
+ goto failed;
+ } else if (os_strncmp(pos, "ssha512:", 8) == 0) {
+ pos += 8;
+ if (hostapd_config_eap_user_salted(user, "sha512", 64,
+ &pos,
+ line, fname) < 0)
+ goto failed;
} else {
pos2 = pos;
while (*pos2 != '\0' && *pos2 != ' ' &&
@@ -522,19 +609,15 @@ static int hostapd_config_read_eap_user(const char *fname,
fclose(f);
if (ret == 0) {
- user = conf->eap_user;
- while (user) {
- struct hostapd_eap_user *prev;
-
- prev = user;
- user = user->next;
- hostapd_config_free_eap_user(prev);
- }
+ hostapd_config_free_eap_users(conf->eap_user);
conf->eap_user = new_user;
+ } else {
+ hostapd_config_free_eap_users(new_user);
}
return ret;
}
+
#endif /* EAP_SERVER */
@@ -684,12 +767,16 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
val |= WPA_KEY_MGMT_PSK;
else if (os_strcmp(start, "WPA-EAP") == 0)
val |= WPA_KEY_MGMT_IEEE8021X;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
else if (os_strcmp(start, "FT-PSK") == 0)
val |= WPA_KEY_MGMT_FT_PSK;
else if (os_strcmp(start, "FT-EAP") == 0)
val |= WPA_KEY_MGMT_FT_IEEE8021X;
-#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SHA384
+ else if (os_strcmp(start, "FT-EAP-SHA384") == 0)
+ val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
val |= WPA_KEY_MGMT_PSK_SHA256;
@@ -710,6 +797,30 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+ else if (os_strcmp(start, "FILS-SHA256") == 0)
+ val |= WPA_KEY_MGMT_FILS_SHA256;
+ else if (os_strcmp(start, "FILS-SHA384") == 0)
+ val |= WPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_IEEE80211R_AP
+ else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
+ val |= WPA_KEY_MGMT_FT_FILS_SHA256;
+ else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
+ val |= WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ else if (os_strcmp(start, "OWE") == 0)
+ val |= WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (os_strcmp(start, "DPP") == 0)
+ val |= WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (os_strcmp(start, "OSEN") == 0)
+ val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -755,17 +866,34 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
{
size_t len = os_strlen(val);
- if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL)
+ if (keyidx < 0 || keyidx > 3)
+ return -1;
+
+ if (len == 0) {
+ int i, set = 0;
+
+ bin_clear_free(wep->key[keyidx], wep->len[keyidx]);
+ wep->key[keyidx] = NULL;
+ wep->len[keyidx] = 0;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (wep->key[i])
+ set++;
+ }
+ if (!set)
+ wep->keys_set = 0;
+ return 0;
+ }
+
+ if (wep->key[keyidx] != NULL)
return -1;
if (val[0] == '"') {
if (len < 2 || val[len - 1] != '"')
return -1;
len -= 2;
- wep->key[keyidx] = os_malloc(len);
+ wep->key[keyidx] = os_memdup(val + 1, len);
if (wep->key[keyidx] == NULL)
return -1;
- os_memcpy(wep->key[keyidx], val + 1, len);
wep->len[keyidx] = len;
} else {
if (len & 1)
@@ -978,7 +1106,27 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf,
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+
+static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
+{
+ u8 oldkey[16];
+ int ret;
+
+ if (!hexstr2bin(pos, key, key_len))
+ return 0;
+
+ /* Try to use old short key for backwards compatibility */
+ if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
+ return -1;
+
+ ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
+ key, key_len);
+ os_memset(oldkey, 0, sizeof(oldkey));
+ return ret;
+}
+
+
static int add_r0kh(struct hostapd_bss_config *bss, char *value)
{
struct ft_remote_r0kh *r0kh;
@@ -1012,7 +1160,7 @@ static int add_r0kh(struct hostapd_bss_config *bss, char *value)
os_memcpy(r0kh->id, pos, r0kh->id_len);
pos = next;
- if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
+ if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
os_free(r0kh);
return -1;
@@ -1057,7 +1205,7 @@ static int add_r1kh(struct hostapd_bss_config *bss, char *value)
}
pos = next;
- if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
+ if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
os_free(r1kh);
return -1;
@@ -1068,7 +1216,7 @@ static int add_r1kh(struct hostapd_bss_config *bss, char *value)
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211N
@@ -1085,6 +1233,12 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf,
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
conf->secondary_channel = 1;
}
+ if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) {
+ conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ conf->ht40_plus_minus_allowed = 1;
+ }
+ if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]"))
+ conf->secondary_channel = 0;
if (os_strstr(capab, "[SMPS-STATIC]")) {
conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
@@ -1307,6 +1461,44 @@ static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
}
+static int parse_venue_url(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ char *sep;
+ size_t nlen;
+ struct hostapd_venue_url *url;
+ int ret = -1;
+
+ sep = os_strchr(pos, ':');
+ if (!sep)
+ goto fail;
+ *sep++ = '\0';
+
+ nlen = os_strlen(sep);
+ if (nlen > 254)
+ goto fail;
+
+ url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1,
+ sizeof(struct hostapd_venue_url));
+ if (!url)
+ goto fail;
+
+ bss->venue_url = url;
+ url = &bss->venue_url[bss->venue_url_count++];
+
+ url->venue_number = atoi(pos);
+ url->url_len = nlen;
+ os_memcpy(url->url, sep, nlen);
+
+ ret = 0;
+fail:
+ if (ret)
+ wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'",
+ line, pos);
+ return ret;
+}
+
+
static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
int line)
{
@@ -1857,6 +2049,24 @@ static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
}
+static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (bss->last_osu == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+ return -1;
+ }
+
+ os_free(bss->last_osu->osu_nai2);
+ bss->last_osu->osu_nai2 = os_strdup(pos);
+ if (bss->last_osu->osu_nai2 == NULL)
+ return -1;
+ bss->hs20_osu_providers_nai_count++;
+
+ return 0;
+}
+
+
static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
int line)
{
@@ -1916,6 +2126,25 @@ static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
return 0;
}
+
+static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ char **n;
+
+ n = os_realloc_array(bss->hs20_operator_icon,
+ bss->hs20_operator_icon_count + 1, sizeof(char *));
+ if (!n)
+ return -1;
+ bss->hs20_operator_icon = n;
+ bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos);
+ if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count])
+ return -1;
+ bss->hs20_operator_icon_count++;
+
+ return 0;
+}
+
#endif /* CONFIG_HS20 */
@@ -1986,6 +2215,118 @@ static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf,
}
+#ifdef CONFIG_FILS
+static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val)
+{
+ struct fils_realm *realm;
+ size_t len;
+
+ len = os_strlen(val);
+ realm = os_zalloc(sizeof(*realm) + len + 1);
+ if (!realm)
+ return -1;
+
+ os_memcpy(realm->realm, val, len);
+ if (fils_domain_name_hash(val, realm->hash) < 0) {
+ os_free(realm);
+ return -1;
+ }
+ dl_list_add_tail(&bss->fils_realms, &realm->list);
+
+ return 0;
+}
+#endif /* CONFIG_FILS */
+
+
+#ifdef EAP_SERVER
+static unsigned int parse_tls_flags(const char *val)
+{
+ unsigned int flags = 0;
+
+ /* Disable TLS v1.3 by default for now to avoid interoperability issue.
+ * This can be enabled by default once the implementation has been fully
+ * completed and tested with other implementations. */
+ flags |= TLS_CONN_DISABLE_TLSv1_3;
+
+ if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]"))
+ flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+ if (os_strstr(val, "[DISABLE-TIME-CHECKS]"))
+ flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+ if (os_strstr(val, "[DISABLE-TLSv1.0]"))
+ flags |= TLS_CONN_DISABLE_TLSv1_0;
+ if (os_strstr(val, "[DISABLE-TLSv1.1]"))
+ flags |= TLS_CONN_DISABLE_TLSv1_1;
+ if (os_strstr(val, "[DISABLE-TLSv1.2]"))
+ flags |= TLS_CONN_DISABLE_TLSv1_2;
+ if (os_strstr(val, "[DISABLE-TLSv1.3]"))
+ flags |= TLS_CONN_DISABLE_TLSv1_3;
+ if (os_strstr(val, "[ENABLE-TLSv1.3]"))
+ flags &= ~TLS_CONN_DISABLE_TLSv1_3;
+ if (os_strstr(val, "[SUITEB]"))
+ flags |= TLS_CONN_SUITEB;
+ if (os_strstr(val, "[SUITEB-NO-ECDH]"))
+ flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB;
+
+ return flags;
+}
+#endif /* EAP_SERVER */
+
+
+#ifdef CONFIG_SAE
+static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
+{
+ struct sae_password_entry *pw;
+ const char *pos = val, *pos2, *end = NULL;
+
+ pw = os_zalloc(sizeof(*pw));
+ if (!pw)
+ return -1;
+ os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */
+
+ pos2 = os_strstr(pos, "|mac=");
+ if (pos2) {
+ end = pos2;
+ pos2 += 5;
+ if (hwaddr_aton(pos2, pw->peer_addr) < 0)
+ goto fail;
+ pos = pos2 + ETH_ALEN * 3 - 1;
+ }
+
+ pos2 = os_strstr(pos, "|id=");
+ if (pos2) {
+ if (!end)
+ end = pos2;
+ pos2 += 4;
+ pw->identifier = os_strdup(pos2);
+ if (!pw->identifier)
+ goto fail;
+ }
+
+ if (!end) {
+ pw->password = os_strdup(val);
+ if (!pw->password)
+ goto fail;
+ } else {
+ pw->password = os_malloc(end - val + 1);
+ if (!pw->password)
+ goto fail;
+ os_memcpy(pw->password, val, end - val);
+ pw->password[end - val] = '\0';
+ }
+
+ pw->next = bss->sae_passwords;
+ bss->sae_passwords = pw;
+
+ return 0;
+fail:
+ str_clear_free(pw->password);
+ os_free(pw->identifier);
+ os_free(pw);
+ return -1;
+}
+#endif /* CONFIG_SAE */
+
+
static int hostapd_config_fill(struct hostapd_config *conf,
struct hostapd_bss_config *bss,
const char *buf, char *pos, int line)
@@ -2001,20 +2342,21 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
} else if (os_strcmp(buf, "driver") == 0) {
int j;
- /* clear to get error below if setting is invalid */
- conf->driver = NULL;
+ const struct wpa_driver_ops *driver = NULL;
+
for (j = 0; wpa_drivers[j]; j++) {
if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
- conf->driver = wpa_drivers[j];
+ driver = wpa_drivers[j];
break;
}
}
- if (conf->driver == NULL) {
+ if (!driver) {
wpa_printf(MSG_ERROR,
"Line %d: invalid/unknown driver '%s'",
line, pos);
return 1;
}
+ conf->driver = driver;
} else if (os_strcmp(buf, "driver_params") == 0) {
os_free(conf->driver_params);
conf->driver_params = os_strdup(pos);
@@ -2058,13 +2400,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "utf8_ssid") == 0) {
bss->ssid.utf8_ssid = atoi(pos) > 0;
} else if (os_strcmp(buf, "macaddr_acl") == 0) {
- bss->macaddr_acl = atoi(pos);
- if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
- bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
- bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ enum macaddr_acl acl = atoi(pos);
+
+ if (acl != ACCEPT_UNLESS_DENIED &&
+ acl != DENY_UNLESS_ACCEPTED &&
+ acl != USE_EXTERNAL_RADIUS_AUTH) {
wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
- line, bss->macaddr_acl);
+ line, acl);
+ return 1;
}
+ bss->macaddr_acl = acl;
} else if (os_strcmp(buf, "accept_mac_file") == 0) {
if (hostapd_config_read_maclist(pos, &bss->accept_mac,
&bss->num_accept_mac)) {
@@ -2091,8 +2436,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->skip_inactivity_poll = atoi(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
os_memcpy(conf->country, pos, 2);
- /* FIX: make this configurable */
- conf->country[2] = ' ';
+ } else if (os_strcmp(buf, "country3") == 0) {
+ conf->country[2] = strtol(pos, NULL, 16);
} else if (os_strcmp(buf, "ieee80211d") == 0) {
conf->ieee80211d = atoi(pos);
} else if (os_strcmp(buf, "ieee80211h") == 0) {
@@ -2100,13 +2445,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "ieee8021x") == 0) {
bss->ieee802_1x = atoi(pos);
} else if (os_strcmp(buf, "eapol_version") == 0) {
- bss->eapol_version = atoi(pos);
- if (bss->eapol_version < 1 || bss->eapol_version > 2) {
+ int eapol_version = atoi(pos);
+
+ if (eapol_version < 1 || eapol_version > 2) {
wpa_printf(MSG_ERROR,
"Line %d: invalid EAPOL version (%d): '%s'.",
- line, bss->eapol_version, pos);
+ line, eapol_version, pos);
return 1;
}
+ bss->eapol_version = eapol_version;
wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
#ifdef EAP_SERVER
} else if (os_strcmp(buf, "eap_authenticator") == 0) {
@@ -2133,6 +2480,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->check_crl = 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) {
+ bss->tls_flags = parse_tls_flags(pos);
} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
os_free(bss->ocsp_stapling_response);
bss->ocsp_stapling_response = os_strdup(pos);
@@ -2207,8 +2556,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "pwd_group") == 0) {
bss->pwd_group = atoi(pos);
#endif /* EAP_SERVER_PWD */
+#ifdef CONFIG_ERP
} else if (os_strcmp(buf, "eap_server_erp") == 0) {
bss->eap_server_erp = atoi(pos);
+#endif /* CONFIG_ERP */
#endif /* EAP_SERVER */
} else if (os_strcmp(buf, "eap_message") == 0) {
char *term;
@@ -2234,24 +2585,25 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->erp_domain);
bss->erp_domain = os_strdup(pos);
} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
- bss->default_wep_key_len = atoi(pos);
- if (bss->default_wep_key_len > 13) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)",
- line,
- (unsigned long) bss->default_wep_key_len,
- (unsigned long)
- bss->default_wep_key_len * 8);
+ int val = atoi(pos);
+
+ if (val < 0 || val > 13) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid WEP key len %d (= %d bits)",
+ line, val, val * 8);
return 1;
}
+ bss->default_wep_key_len = val;
} else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
- bss->individual_wep_key_len = atoi(pos);
- if (bss->individual_wep_key_len < 0 ||
- bss->individual_wep_key_len > 13) {
- wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)",
- line, bss->individual_wep_key_len,
- bss->individual_wep_key_len * 8);
+ int val = atoi(pos);
+
+ if (val < 0 || val > 13) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid WEP key len %d (= %d bits)",
+ line, val, val * 8);
return 1;
}
+ bss->individual_wep_key_len = val;
} else if (os_strcmp(buf, "wep_rekey_period") == 0) {
bss->wep_rekeying_period = atoi(pos);
if (bss->wep_rekeying_period < 0) {
@@ -2433,12 +2785,37 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->wpa = atoi(pos);
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos);
+ bss->wpa_group_rekey_set = 1;
} else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
bss->wpa_strict_rekey = atoi(pos);
} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
bss->wpa_gmk_rekey = atoi(pos);
} else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
bss->wpa_ptk_rekey = atoi(pos);
+ } else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
+ char *endp;
+ unsigned long val = strtoul(pos, &endp, 0);
+
+ if (*endp || val < 1 || val > (u32) -1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295",
+ line, val);
+ return 1;
+ }
+ bss->wpa_group_update_count = (u32) val;
+ } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) {
+ char *endp;
+ unsigned long val = strtoul(pos, &endp, 0);
+
+ if (*endp || val < 1 || val > (u32) -1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295",
+ line, val);
+ return 1;
+ }
+ bss->wpa_pairwise_update_count = (u32) val;
+ } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
+ bss->wpa_disable_eapol_key_retries = atoi(pos);
} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
int len = os_strlen(pos);
if (len < 8 || len > 63) {
@@ -2497,7 +2874,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
if (bss->wpa_pairwise &
(WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
- bss->wpa_pairwise, pos);
+ line, pos);
return 1;
}
} else if (os_strcmp(buf, "rsn_pairwise") == 0) {
@@ -2507,7 +2884,21 @@ static int hostapd_config_fill(struct hostapd_config *conf,
if (bss->rsn_pairwise &
(WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
- bss->rsn_pairwise, pos);
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "group_cipher") == 0) {
+ bss->group_cipher = hostapd_config_parse_cipher(line, pos);
+ if (bss->group_cipher == -1 || bss->group_cipher == 0)
+ return 1;
+ if (bss->group_cipher != WPA_CIPHER_TKIP &&
+ bss->group_cipher != WPA_CIPHER_CCMP &&
+ bss->group_cipher != WPA_CIPHER_GCMP &&
+ bss->group_cipher != WPA_CIPHER_GCMP_256 &&
+ bss->group_cipher != WPA_CIPHER_CCMP_256) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: unsupported group cipher suite '%s'",
+ line, pos);
return 1;
}
#ifdef CONFIG_RSN_PREAUTH
@@ -2517,11 +2908,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->rsn_preauth_interfaces);
bss->rsn_preauth_interfaces = os_strdup(pos);
#endif /* CONFIG_RSN_PREAUTH */
-#ifdef CONFIG_PEERKEY
} else if (os_strcmp(buf, "peerkey") == 0) {
- bss->peerkey = atoi(pos);
-#endif /* CONFIG_PEERKEY */
-#ifdef CONFIG_IEEE80211R
+ wpa_printf(MSG_INFO,
+ "Line %d: Obsolete peerkey parameter ignored", line);
+#ifdef CONFIG_IEEE80211R_AP
} else if (os_strcmp(buf, "mobility_domain") == 0) {
if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
hexstr2bin(pos, bss->mobility_domain,
@@ -2540,9 +2930,22 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
} else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
+ /* DEPRECATED: Use ft_r0_key_lifetime instead. */
+ bss->r0_key_lifetime = atoi(pos) * 60;
+ } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) {
bss->r0_key_lifetime = atoi(pos);
+ } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) {
+ bss->r1_max_key_lifetime = atoi(pos);
} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
bss->reassociation_deadline = atoi(pos);
+ } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
+ bss->rkh_pos_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "rkh_neg_timeout") == 0) {
+ bss->rkh_neg_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) {
+ bss->rkh_pull_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "rkh_pull_retries") == 0) {
+ bss->rkh_pull_retries = atoi(pos);
} else if (os_strcmp(buf, "r0kh") == 0) {
if (add_r0kh(bss, pos) < 0) {
wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
@@ -2559,7 +2962,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->pmk_r1_push = atoi(pos);
} else if (os_strcmp(buf, "ft_over_ds") == 0) {
bss->ft_over_ds = atoi(pos);
-#endif /* CONFIG_IEEE80211R */
+ } else if (os_strcmp(buf, "ft_psk_generate_local") == 0) {
+ bss->ft_psk_generate_local = atoi(pos);
+#endif /* CONFIG_IEEE80211R_AP */
#ifndef CONFIG_NO_CTRL_IFACE
} else if (os_strcmp(buf, "ctrl_interface") == 0) {
os_free(bss->ctrl_interface);
@@ -2637,6 +3042,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
+ conf->acs_exclude_dfs = atoi(pos);
} else if (os_strcmp(buf, "channel") == 0) {
if (os_strcmp(pos, "acs_survey") == 0) {
#ifndef CONFIG_ACS
@@ -2687,21 +3094,34 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
#endif /* CONFIG_ACS */
} else if (os_strcmp(buf, "dtim_period") == 0) {
- bss->dtim_period = atoi(pos);
- if (bss->dtim_period < 1 || bss->dtim_period > 255) {
+ int val = atoi(pos);
+
+ if (val < 1 || val > 255) {
wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
- line, bss->dtim_period);
+ line, val);
return 1;
}
+ bss->dtim_period = val;
} else if (os_strcmp(buf, "bss_load_update_period") == 0) {
- bss->bss_load_update_period = atoi(pos);
- if (bss->bss_load_update_period < 0 ||
- bss->bss_load_update_period > 100) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 100) {
wpa_printf(MSG_ERROR,
"Line %d: invalid bss_load_update_period %d",
- line, bss->bss_load_update_period);
+ line, val);
return 1;
}
+ bss->bss_load_update_period = val;
+ } else if (os_strcmp(buf, "chan_util_avg_period") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid chan_util_avg_period",
+ line);
+ return 1;
+ }
+ bss->chan_util_avg_period = val;
} else if (os_strcmp(buf, "rts_threshold") == 0) {
conf->rts_threshold = atoi(pos);
if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
@@ -2741,6 +3161,40 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line);
return 1;
}
+ } else if (os_strcmp(buf, "beacon_rate") == 0) {
+ int val;
+
+ if (os_strncmp(pos, "ht:", 3) == 0) {
+ val = atoi(pos + 3);
+ if (val < 0 || val > 31) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid beacon_rate HT-MCS %d",
+ line, val);
+ return 1;
+ }
+ conf->rate_type = BEACON_RATE_HT;
+ conf->beacon_rate = val;
+ } else if (os_strncmp(pos, "vht:", 4) == 0) {
+ val = atoi(pos + 4);
+ if (val < 0 || val > 9) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid beacon_rate VHT-MCS %d",
+ line, val);
+ return 1;
+ }
+ conf->rate_type = BEACON_RATE_VHT;
+ conf->beacon_rate = val;
+ } else {
+ val = atoi(pos);
+ if (val < 10 || val > 10000) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid legacy beacon_rate %d",
+ line, val);
+ return 1;
+ }
+ conf->rate_type = BEACON_RATE_LEGACY;
+ conf->beacon_rate = val;
+ }
} else if (os_strcmp(buf, "preamble") == 0) {
if (atoi(pos))
conf->preamble = SHORT_PREAMBLE;
@@ -2898,6 +3352,24 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "use_sta_nsts") == 0) {
bss->use_sta_nsts = atoi(pos);
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ } else if (os_strcmp(buf, "ieee80211ax") == 0) {
+ conf->ieee80211ax = atoi(pos);
+ } else if (os_strcmp(buf, "he_su_beamformer") == 0) {
+ conf->he_phy_capab.he_su_beamformer = atoi(pos);
+ } else if (os_strcmp(buf, "he_su_beamformee") == 0) {
+ conf->he_phy_capab.he_su_beamformee = atoi(pos);
+ } else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
+ conf->he_phy_capab.he_mu_beamformer = atoi(pos);
+ } else if (os_strcmp(buf, "he_bss_color") == 0) {
+ conf->he_op.he_bss_color = atoi(pos);
+ } else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
+ conf->he_op.he_default_pe_duration = atoi(pos);
+ } else if (os_strcmp(buf, "he_twt_required") == 0) {
+ 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);
+#endif /* CONFIG_IEEE80211AX */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
@@ -2978,7 +3450,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
} else if (os_strcmp(buf, "ap_pin") == 0) {
os_free(bss->ap_pin);
- bss->ap_pin = os_strdup(pos);
+ if (*pos == '\0')
+ bss->ap_pin = NULL;
+ else
+ bss->ap_pin = os_strdup(pos);
} else if (os_strcmp(buf, "skip_cred_build") == 0) {
bss->skip_cred_build = atoi(pos);
} else if (os_strcmp(buf, "extra_cred") == 0) {
@@ -3089,12 +3564,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->time_zone = os_strdup(pos);
if (bss->time_zone == NULL)
return 1;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
bss->wnm_sleep_mode = atoi(pos);
+ } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) {
+ bss->wnm_sleep_mode_no_keys = atoi(pos);
} else if (os_strcmp(buf, "bss_transition") == 0) {
bss->bss_transition = atoi(pos);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_INTERWORKING
} else if (os_strcmp(buf, "interworking") == 0) {
bss->interworking = atoi(pos);
@@ -3132,6 +3609,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "venue_name") == 0) {
if (parse_venue_name(bss, pos, line) < 0)
return 1;
+ } else if (os_strcmp(buf, "venue_url") == 0) {
+ if (parse_venue_url(bss, pos, line) < 0)
+ return 1;
} else if (os_strcmp(buf, "network_auth_type") == 0) {
u8 auth_type;
u16 redirect_url_len;
@@ -3210,7 +3690,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
if (parse_anqp_elem(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
- bss->gas_frag_limit = atoi(pos);
+ int val = atoi(pos);
+
+ if (val <= 0) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid gas_frag_limit '%s'",
+ line, pos);
+ return 1;
+ }
+ bss->gas_frag_limit = val;
} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
bss->gas_comeback_delay = atoi(pos);
} else if (os_strcmp(buf, "qos_map_set") == 0) {
@@ -3291,6 +3779,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "osu_nai") == 0) {
if (hs20_parse_osu_nai(bss, pos, line) < 0)
return 1;
+ } else if (os_strcmp(buf, "osu_nai2") == 0) {
+ if (hs20_parse_osu_nai2(bss, pos, line) < 0)
+ return 1;
} else if (os_strcmp(buf, "osu_method_list") == 0) {
if (hs20_parse_osu_method_list(bss, pos, line) < 0)
return 1;
@@ -3300,15 +3791,30 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "osu_service_desc") == 0) {
if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
return 1;
+ } else if (os_strcmp(buf, "operator_icon") == 0) {
+ if (hs20_parse_operator_icon(bss, pos, line) < 0)
+ return 1;
} else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
os_free(bss->subscr_remediation_url);
bss->subscr_remediation_url = os_strdup(pos);
} else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
bss->subscr_remediation_method = atoi(pos);
+ } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) {
+ os_free(bss->t_c_filename);
+ bss->t_c_filename = os_strdup(pos);
+ } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) {
+ bss->t_c_timestamp = strtol(pos, NULL, 0);
+ } 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);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
} else if (os_strcmp(buf, "mbo") == 0) {
bss->mbo_enabled = atoi(pos);
+ } else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) {
+ bss->mbo_cell_data_conn_pref = atoi(pos);
+ } else if (os_strcmp(buf, "oce") == 0) {
+ bss->oce = atoi(pos);
#endif /* CONFIG_MBO */
#ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \
@@ -3377,7 +3883,20 @@ static int hostapd_config_fill(struct hostapd_config *conf,
wpabuf_free(bss->own_ie_override);
bss->own_ie_override = tmp;
+ } else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
+ bss->sae_reflection_attack = atoi(pos);
+ } else if (os_strcmp(buf, "sae_commit_override") == 0) {
+ wpabuf_free(bss->sae_commit_override);
+ bss->sae_commit_override = wpabuf_parse_bin(pos);
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_SAE
+ } else if (os_strcmp(buf, "sae_password") == 0) {
+ if (parse_sae_password(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password",
+ line);
+ return 1;
+ }
+#endif /* CONFIG_SAE */
} else if (os_strcmp(buf, "vendor_elements") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
return 1;
@@ -3386,6 +3905,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
bss->sae_anti_clogging_threshold = atoi(pos);
+ } else if (os_strcmp(buf, "sae_sync") == 0) {
+ bss->sae_sync = atoi(pos);
} else if (os_strcmp(buf, "sae_groups") == 0) {
if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
wpa_printf(MSG_ERROR,
@@ -3393,6 +3914,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "sae_require_mfp") == 0) {
+ bss->sae_require_mfp = atoi(pos);
} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
int val = atoi(pos);
if (val < 0 || val > 255) {
@@ -3478,19 +4001,116 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "lci") == 0) {
wpabuf_free(conf->lci);
conf->lci = wpabuf_parse_bin(pos);
+ if (conf->lci && wpabuf_len(conf->lci) == 0) {
+ wpabuf_free(conf->lci);
+ conf->lci = NULL;
+ }
} else if (os_strcmp(buf, "civic") == 0) {
wpabuf_free(conf->civic);
conf->civic = wpabuf_parse_bin(pos);
+ if (conf->civic && wpabuf_len(conf->civic) == 0) {
+ wpabuf_free(conf->civic);
+ conf->civic = NULL;
+ }
} else if (os_strcmp(buf, "rrm_neighbor_report") == 0) {
if (atoi(pos))
bss->radio_measurements[0] |=
WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+ } else if (os_strcmp(buf, "rrm_beacon_report") == 0) {
+ if (atoi(pos))
+ bss->radio_measurements[0] |=
+ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
} else if (os_strcmp(buf, "gas_address3") == 0) {
bss->gas_address3 = atoi(pos);
+ } else if (os_strcmp(buf, "stationary_ap") == 0) {
+ conf->stationary_ap = atoi(pos);
} else if (os_strcmp(buf, "ftm_responder") == 0) {
bss->ftm_responder = atoi(pos);
} else if (os_strcmp(buf, "ftm_initiator") == 0) {
bss->ftm_initiator = atoi(pos);
+#ifdef CONFIG_FILS
+ } else if (os_strcmp(buf, "fils_cache_id") == 0) {
+ if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid fils_cache_id '%s'",
+ line, pos);
+ return 1;
+ }
+ bss->fils_cache_id_set = 1;
+ } else if (os_strcmp(buf, "fils_realm") == 0) {
+ if (parse_fils_realm(bss, pos) < 0)
+ return 1;
+ } else if (os_strcmp(buf, "fils_dh_group") == 0) {
+ bss->fils_dh_group = atoi(pos);
+ } else if (os_strcmp(buf, "dhcp_server") == 0) {
+ if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid IP address '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) {
+ bss->dhcp_rapid_commit_proxy = atoi(pos);
+ } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) {
+ bss->fils_hlp_wait_time = atoi(pos);
+ } else if (os_strcmp(buf, "dhcp_server_port") == 0) {
+ bss->dhcp_server_port = atoi(pos);
+ } else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
+ bss->dhcp_relay_port = atoi(pos);
+#endif /* CONFIG_FILS */
+ } else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
+ bss->multicast_to_unicast = atoi(pos);
+ } else if (os_strcmp(buf, "broadcast_deauth") == 0) {
+ bss->broadcast_deauth = atoi(pos);
+#ifdef CONFIG_DPP
+ } else if (os_strcmp(buf, "dpp_connector") == 0) {
+ os_free(bss->dpp_connector);
+ bss->dpp_connector = os_strdup(pos);
+ } else if (os_strcmp(buf, "dpp_netaccesskey") == 0) {
+ if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos))
+ return 1;
+ } else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) {
+ bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0);
+ } else if (os_strcmp(buf, "dpp_csign") == 0) {
+ if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
+ return 1;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_OWE
+ } else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
+ if (hwaddr_aton(pos, bss->owe_transition_bssid)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid owe_transition_bssid",
+ line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "owe_transition_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->owe_transition_ssid, str, slen);
+ bss->owe_transition_ssid_len = slen;
+ os_free(str);
+ } else if (os_strcmp(buf, "owe_transition_ifname") == 0) {
+ os_strlcpy(bss->owe_transition_ifname, pos,
+ sizeof(bss->owe_transition_ifname));
+ } else if (os_strcmp(buf, "owe_groups") == 0) {
+ if (hostapd_parse_intlist(&bss->owe_groups, pos)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid owe_groups value '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
+ bss->coloc_intf_reporting = atoi(pos);
+#endif /* CONFIG_OWE */
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index c98bdb683ba1..9830f5a2232f 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -13,5 +13,10 @@ struct hostapd_config * hostapd_config_read(const char *fname);
int hostapd_set_iface(struct hostapd_config *conf,
struct hostapd_bss_config *bss, const char *field,
char *value);
+int hostapd_acl_comp(const void *a, const void *b);
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+ int vlan_id, const u8 *addr);
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+ const u8 *addr);
#endif /* CONFIG_FILE_H */
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index d7db4a7c3c48..75f12e543f21 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1,6 +1,6 @@
/*
* hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2015, Jouni Malinen
+ * Copyright (c) 2004-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,6 +29,10 @@
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "common/ctrl_iface_common.h"
+#ifdef CONFIG_DPP
+#include "common/dpp.h"
+#endif /* CONFIG_DPP */
+#include "common/wpa_ctrl.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eapol_auth/eapol_auth_sm.h"
@@ -50,6 +54,7 @@
#include "ap/beacon.h"
#include "ap/neighbor_db.h"
#include "ap/rrm.h"
+#include "ap/dpp_hostapd.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
#include "fst/fst_ctrl_iface.h"
@@ -76,9 +81,9 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
struct sockaddr_storage *from,
- socklen_t fromlen)
+ socklen_t fromlen, const char *input)
{
- return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen);
+ return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input);
}
@@ -763,7 +768,7 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
#endif /* CONFIG_INTERWORKING */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
const char *cmd)
@@ -838,7 +843,7 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
char *url = NULL;
int ret;
u8 nei_rep[1000];
- u8 *nei_pos = nei_rep;
+ int nei_len;
u8 mbo[10];
size_t mbo_len = 0;
@@ -888,99 +893,10 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
}
-
- /*
- * BSS Transition Candidate List Entries - Neighbor Report elements
- * neighbor=,,,
- * ,[,]
- */
- pos = cmd;
- while (pos) {
- u8 *nei_start;
- long int val;
- char *endptr, *tmp;
-
- pos = os_strstr(pos, " neighbor=");
- if (!pos)
- break;
- if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
- wpa_printf(MSG_DEBUG,
- "Not enough room for additional neighbor");
- return -1;
- }
- pos += 10;
-
- nei_start = nei_pos;
- *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
- nei_pos++; /* length to be filled in */
-
- if (hwaddr_aton(pos, nei_pos)) {
- wpa_printf(MSG_DEBUG, "Invalid BSSID");
- return -1;
- }
- nei_pos += ETH_ALEN;
- pos += 17;
- if (*pos != ',') {
- wpa_printf(MSG_DEBUG, "Missing BSSID Information");
- return -1;
- }
- pos++;
-
- val = strtol(pos, &endptr, 0);
- WPA_PUT_LE32(nei_pos, val);
- nei_pos += 4;
- if (*endptr != ',') {
- wpa_printf(MSG_DEBUG, "Missing Operating Class");
- return -1;
- }
- pos = endptr + 1;
-
- *nei_pos++ = atoi(pos); /* Operating Class */
- pos = os_strchr(pos, ',');
- if (pos == NULL) {
- wpa_printf(MSG_DEBUG, "Missing Channel Number");
- return -1;
- }
- pos++;
-
- *nei_pos++ = atoi(pos); /* Channel Number */
- pos = os_strchr(pos, ',');
- if (pos == NULL) {
- wpa_printf(MSG_DEBUG, "Missing PHY Type");
- return -1;
- }
- pos++;
-
- *nei_pos++ = atoi(pos); /* PHY Type */
- end = os_strchr(pos, ' ');
- tmp = os_strchr(pos, ',');
- if (tmp && (!end || tmp < end)) {
- /* Optional Subelements (hexdump) */
- size_t len;
-
- pos = tmp + 1;
- end = os_strchr(pos, ' ');
- if (end)
- len = end - pos;
- else
- len = os_strlen(pos);
- if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
- wpa_printf(MSG_DEBUG,
- "Not enough room for neighbor subelements");
- return -1;
- }
- if (len & 0x01 ||
- hexstr2bin(pos, nei_pos, len / 2) < 0) {
- wpa_printf(MSG_DEBUG,
- "Invalid neighbor subelement info");
- return -1;
- }
- nei_pos += len / 2;
- pos = end;
- }
-
- nei_start[1] = nei_pos - nei_start - 2;
- }
+ nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+ sizeof(nei_rep));
+ if (nei_len < 0)
+ return -1;
pos = os_strstr(cmd, " url=");
if (pos) {
@@ -1017,14 +933,16 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
if (ret != 3) {
wpa_printf(MSG_DEBUG,
"MBO requires three arguments: mbo=::");
- return -1;
+ ret = -1;
+ goto fail;
}
if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
wpa_printf(MSG_DEBUG,
"Invalid MBO transition reason code %u",
mbo_reason);
- return -1;
+ ret = -1;
+ goto fail;
}
/* Valid values for Cellular preference are: 0, 1, 255 */
@@ -1032,7 +950,8 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG,
"Invalid MBO cellular capability %u",
cell_pref);
- return -1;
+ ret = -1;
+ goto fail;
}
if (reassoc_delay > 65535 ||
@@ -1040,7 +959,8 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
!(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
wpa_printf(MSG_DEBUG,
"MBO: Assoc retry delay is only valid in disassoc imminent mode");
- return -1;
+ ret = -1;
+ goto fail;
}
*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
@@ -1063,14 +983,52 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
valid_int, bss_term_dur, url,
- nei_pos > nei_rep ? nei_rep : NULL,
- nei_pos - nei_rep, mbo_len ? mbo : NULL,
- mbo_len);
+ nei_len ? nei_rep : NULL, nei_len,
+ mbo_len ? mbo : NULL, mbo_len);
+#ifdef CONFIG_MBO
+fail:
+#endif /* CONFIG_MBO */
os_free(url);
return ret;
}
-#endif /* CONFIG_WNM */
+
+static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ const char *pos;
+ unsigned int auto_report, timeout;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for Collocated Interference Request",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ auto_report = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ timeout = atoi(pos);
+
+ return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout);
+}
+
+#endif /* CONFIG_WNM_AP */
static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
@@ -1096,7 +1054,7 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
return pos - buf;
pos += ret;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
ret = os_snprintf(pos, end - pos, "FT-PSK ");
if (os_snprintf_error(end - pos, ret))
@@ -1109,6 +1067,14 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
return pos - buf;
pos += ret;
}
+#ifdef CONFIG_SHA384
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SAE
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
ret = os_snprintf(pos, end - pos, "FT-SAE ");
@@ -1117,7 +1083,21 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
pos += ret;
}
#endif /* CONFIG_SAE */
-#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_FILS */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
@@ -1154,6 +1134,38 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
return pos - buf;
pos += ret;
}
+#ifdef CONFIG_FILS
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ ret = os_snprintf(pos, end - pos, "FILS-SHA256 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+ ret = os_snprintf(pos, end - pos, "OWE ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ ret = os_snprintf(pos, end - pos, "DPP ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_DPP */
if (pos > buf && *(pos - 1) == ' ') {
*(pos - 1) = '\0';
@@ -1282,6 +1294,42 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
}
+static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct vlan_description vlan_id;
+
+ if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
+ return;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac,
+ sta->addr, &vlan_id) ||
+ (vlan_id.notempty &&
+ vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+}
+
+
+static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct vlan_description vlan_id;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, sta->addr,
+ &vlan_id) &&
+ (!vlan_id.notempty ||
+ !vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+}
+
static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
{
char *value;
@@ -1319,19 +1367,27 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
wps_corrupt_pkhash);
#endif /* CONFIG_WPS_TESTING */
-#ifdef CONFIG_INTERWORKING
- } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
- int val = atoi(value);
- if (val <= 0)
- ret = -1;
- else
- hapd->gas_frag_limit = val;
-#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
hapd->ext_mgmt_frame_handling = atoi(value);
} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
hapd->ext_eapol_frame_io = atoi(value);
+#ifdef CONFIG_DPP
+ } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
+ os_free(hapd->dpp_config_obj_override);
+ hapd->dpp_config_obj_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
+ os_free(hapd->dpp_discovery_override);
+ hapd->dpp_discovery_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
+ os_free(hapd->dpp_groups_override);
+ hapd->dpp_groups_override = os_strdup(value);
+ } else if (os_strcasecmp(cmd,
+ "dpp_ignore_netaccesskey_mismatch") == 0) {
+ hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_test") == 0) {
+ dpp_test = atoi(value);
+#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
@@ -1352,39 +1408,26 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
* disallowing station logic.
*/
#endif /* CONFIG_MBO */
+#ifdef CONFIG_DPP
+ } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
+ os_free(hapd->dpp_configurator_params);
+ hapd->dpp_configurator_params = os_strdup(value);
+#endif /* CONFIG_DPP */
} else {
- struct sta_info *sta;
- struct vlan_description vlan_id;
-
ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
if (ret)
return ret;
if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
- for (sta = hapd->sta_list; sta; sta = sta->next) {
- if (hostapd_maclist_found(
- hapd->conf->deny_mac,
- hapd->conf->num_deny_mac, sta->addr,
- &vlan_id) &&
- (!vlan_id.notempty ||
- !vlan_compare(&vlan_id, sta->vlan_desc)))
- ap_sta_disconnect(
- hapd, sta, sta->addr,
- WLAN_REASON_UNSPECIFIED);
- }
- } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED &&
- os_strcasecmp(cmd, "accept_mac_file") == 0) {
- for (sta = hapd->sta_list; sta; sta = sta->next) {
- if (!hostapd_maclist_found(
- hapd->conf->accept_mac,
- hapd->conf->num_accept_mac,
- sta->addr, &vlan_id) ||
- (vlan_id.notempty &&
- vlan_compare(&vlan_id, sta->vlan_desc)))
- ap_sta_disconnect(
- hapd, sta, sta->addr,
- WLAN_REASON_UNSPECIFIED);
- }
+ hostapd_disassoc_deny_mac(hapd);
+ } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
+ hostapd_disassoc_accept_mac(hapd);
+ } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
+ os_strncmp(cmd, "wmm_ac_", 7) == 0) {
+ hapd->parameter_set_count++;
+ if (ieee802_11_update_beacons(hapd->iface))
+ wpa_printf(MSG_DEBUG,
+ "Failed to update beacons with WMM parameters");
}
}
@@ -1534,6 +1577,137 @@ static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
}
+static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
+ char *cmd)
+{
+ char *pos, *param;
+ size_t len;
+ u8 *buf;
+ int stype = 0, ok = 0;
+ union wpa_event_data event;
+
+ if (!hapd->ext_mgmt_frame_handling)
+ return -1;
+
+ /* stype= ok=<0/1> buf= */
+
+ wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
+
+ pos = cmd;
+ param = os_strstr(pos, "stype=");
+ if (param) {
+ param += 6;
+ stype = atoi(param);
+ }
+
+ param = os_strstr(pos, " ok=");
+ if (param) {
+ param += 4;
+ ok = atoi(param);
+ }
+
+ param = os_strstr(pos, " buf=");
+ if (!param)
+ return -1;
+ param += 5;
+
+ len = os_strlen(param);
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (!buf || hexstr2bin(param, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ event.tx_status.type = WLAN_FC_TYPE_MGMT;
+ event.tx_status.data = buf;
+ event.tx_status.data_len = len;
+ event.tx_status.stype = stype;
+ event.tx_status.ack = ok;
+ hapd->ext_mgmt_frame_handling = 0;
+ wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
+ hapd->ext_mgmt_frame_handling = 1;
+
+ os_free(buf);
+
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
+ char *cmd)
+{
+ char *pos, *param;
+ size_t len;
+ u8 *buf;
+ int freq = 0, datarate = 0, ssi_signal = 0;
+ union wpa_event_data event;
+
+ if (!hapd->ext_mgmt_frame_handling)
+ return -1;
+
+ /* freq= datarate= ssi_signal= frame= */
+
+ wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
+
+ pos = cmd;
+ param = os_strstr(pos, "freq=");
+ if (param) {
+ param += 5;
+ freq = atoi(param);
+ }
+
+ param = os_strstr(pos, " datarate=");
+ if (param) {
+ param += 10;
+ datarate = atoi(param);
+ }
+
+ param = os_strstr(pos, " ssi_signal=");
+ if (param) {
+ param += 12;
+ ssi_signal = atoi(param);
+ }
+
+ param = os_strstr(pos, " frame=");
+ if (param == NULL)
+ return -1;
+ param += 7;
+
+ len = os_strlen(param);
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (hexstr2bin(param, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ event.rx_mgmt.freq = freq;
+ event.rx_mgmt.frame = buf;
+ event.rx_mgmt.frame_len = len;
+ event.rx_mgmt.ssi_signal = ssi_signal;
+ event.rx_mgmt.datarate = datarate;
+ hapd->ext_mgmt_frame_handling = 0;
+ wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event);
+ hapd->ext_mgmt_frame_handling = 1;
+
+ os_free(buf);
+
+ return 0;
+}
+
+
static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
{
char *pos;
@@ -1843,6 +2017,245 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
#endif /* WPA_TRACE_BFD */
}
+
+static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
+{
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+ u8 zero[WPA_TK_MAX_LEN];
+
+ os_memset(zero, 0, sizeof(zero));
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+#ifdef CONFIG_IEEE80211W
+ if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
+ if (hapd->last_igtk_alg == WPA_ALG_NONE)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
+
+ /* First, use a zero key to avoid any possible duplicate key
+ * avoidance in the driver. */
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_igtk_alg,
+ broadcast_ether_addr,
+ hapd->last_igtk_key_idx, 1, NULL, 0,
+ zero, hapd->last_igtk_len) < 0)
+ return -1;
+
+ /* Set the previously configured key to reset its TSC */
+ return hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_igtk_alg,
+ broadcast_ether_addr,
+ hapd->last_igtk_key_idx, 1, NULL, 0,
+ hapd->last_igtk,
+ hapd->last_igtk_len);
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ if (is_broadcast_ether_addr(addr)) {
+ if (hapd->last_gtk_alg == WPA_ALG_NONE)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
+
+ /* First, use a zero key to avoid any possible duplicate key
+ * avoidance in the driver. */
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_gtk_alg,
+ broadcast_ether_addr,
+ hapd->last_gtk_key_idx, 1, NULL, 0,
+ zero, hapd->last_gtk_len) < 0)
+ return -1;
+
+ /* Set the previously configured key to reset its TSC */
+ return hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_gtk_alg,
+ broadcast_ether_addr,
+ hapd->last_gtk_key_idx, 1, NULL, 0,
+ hapd->last_gtk, hapd->last_gtk_len);
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return -1;
+
+ if (sta->last_tk_alg == WPA_ALG_NONE)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
+ MAC2STR(sta->addr));
+
+ /* First, use a zero key to avoid any possible duplicate key avoidance
+ * in the driver. */
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+ sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+ zero, sta->last_tk_len) < 0)
+ return -1;
+
+ /* Set the previously configured key to reset its TSC/RSC */
+ return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+ sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+ sta->last_tk, sta->last_tk_len);
+}
+
+
+static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *pos = cmd;
+ enum wpa_alg alg;
+ int idx, set_tx;
+ u8 seq[6], key[WPA_TK_MAX_LEN];
+ size_t key_len;
+
+ /* parameters: alg addr idx set_tx seq key */
+
+ alg = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ if (hwaddr_aton(pos, addr))
+ return -1;
+ pos += 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ idx = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ set_tx = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ if (hexstr2bin(pos, seq, sizeof(seq)) < 0)
+ return -1;
+ pos += 2 * 6;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ key_len = os_strlen(pos) / 2;
+ if (hexstr2bin(pos, key, key_len) < 0)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Set key");
+ return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx,
+ set_tx, seq, 6, key, key_len);
+}
+
+
+static void restore_tk(void *ctx1, void *ctx2)
+{
+ struct hostapd_data *hapd = ctx1;
+ struct sta_info *sta = ctx2;
+
+ wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
+ MAC2STR(sta->addr));
+ /* This does not really restore the TSC properly, so this will result
+ * in replay protection issues for now since there is no clean way of
+ * preventing encryption of a single EAPOL frame. */
+ hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
+ sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
+ sta->last_tk, sta->last_tk_len);
+}
+
+
+static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
+{
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+ int plain = os_strstr(cmd, "plaintext") != NULL;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+ plain = 0; /* no need for special processing */
+ if (plain) {
+ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+ MAC2STR(sta->addr));
+ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+ sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+ NULL, 0);
+ }
+
+ wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
+ return wpa_auth_resend_m1(sta->wpa_sm,
+ os_strstr(cmd, "change-anonce") != NULL,
+ plain ? restore_tk : NULL, hapd, sta);
+}
+
+
+static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
+{
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+ int plain = os_strstr(cmd, "plaintext") != NULL;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+ plain = 0; /* no need for special processing */
+ if (plain) {
+ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+ MAC2STR(sta->addr));
+ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+ sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+ NULL, 0);
+ }
+
+ wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
+ return wpa_auth_resend_m3(sta->wpa_sm,
+ plain ? restore_tk : NULL, hapd, sta);
+}
+
+
+static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+ int plain = os_strstr(cmd, "plaintext") != NULL;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (plain && sta->last_tk_alg == WPA_ALG_NONE)
+ plain = 0; /* no need for special processing */
+ if (plain) {
+ wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
+ MAC2STR(sta->addr));
+ hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
+ sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
+ NULL, 0);
+ }
+
+ wpa_printf(MSG_INFO,
+ "TESTING: Send group M1 for the same GTK and zero RSC to "
+ MACSTR, MAC2STR(sta->addr));
+ return wpa_auth_resend_group_m1(sta->wpa_sm,
+ plain ? restore_tk : NULL, hapd, sta);
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
@@ -1859,6 +2272,11 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
return ret;
for (i = 0; i < iface->num_bss; i++) {
+
+ /* Save CHAN_SWITCH VHT config */
+ hostapd_chan_switch_vht_config(
+ iface->bss[i], settings.freq_params.vht_enabled);
+
ret = hostapd_switch_channel(iface->bss[i], &settings);
if (ret) {
/* FIX: What do we do if CSA fails in the middle of
@@ -2055,8 +2473,9 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
int ret;
os_reltime_sub(&now, &info->last_seen, &age);
- ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
- MAC2STR(info->addr), (unsigned int) age.sec);
+ ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
+ MAC2STR(info->addr), (unsigned int) age.sec,
+ info->ssi_signal);
if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
@@ -2140,11 +2559,52 @@ static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
}
+static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
+ const char *cmd, char *reply,
+ size_t reply_size)
+{
+ u8 addr[ETH_ALEN];
+ const char *pos;
+ struct wpabuf *req;
+ int ret;
+ u8 req_mode = 0;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ if (os_strncmp(pos, "req_mode=", 9) == 0) {
+ int val = hex2byte(pos + 9);
+
+ if (val < 0)
+ return -1;
+ req_mode = val;
+ pos += 11;
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+ }
+ req = wpabuf_parse_bin(pos);
+ if (!req)
+ return -1;
+
+ ret = hostapd_send_beacon_req(hapd, addr, req_mode, req);
+ wpabuf_free(req);
+ if (ret >= 0)
+ ret = os_snprintf(reply, reply_size, "%d", ret);
+ return ret;
+}
+
+
static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
{
struct wpa_ssid_value ssid;
u8 bssid[ETH_ALEN];
struct wpabuf *nr, *lci = NULL, *civic = NULL;
+ int stationary = 0;
char *tmp;
int ret;
@@ -2223,8 +2683,15 @@ static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
}
}
+ if (!buf)
+ goto set;
+
+ if (os_strstr(buf, "stat"))
+ stationary = 1;
+
set:
- ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic);
+ ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
+ stationary);
wpabuf_free(nr);
wpabuf_free(lci);
@@ -2285,6 +2752,80 @@ static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
}
+static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct vlan_description vlan_id;
+
+ if (!(*num))
+ return 0;
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
+ hostapd_remove_acl_mac(acl, num, addr);
+
+ return 0;
+}
+
+
+static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+ int *num)
+{
+ while (*num)
+ hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
+}
+
+
+static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+ char *buf, size_t buflen)
+{
+ int i = 0, len = 0, ret = 0;
+
+ if (!acl)
+ return 0;
+
+ while (i < num) {
+ ret = os_snprintf(buf + len, buflen - len,
+ MACSTR " VLAN_ID=%d\n",
+ MAC2STR(acl[i].addr),
+ acl[i].vlan_id.untagged);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ i++;
+ len += ret;
+ }
+ return len;
+}
+
+
+static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct vlan_description vlan_id;
+ int ret = 0, vlanid = 0;
+ const char *pos;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = os_strstr(cmd, "VLAN_ID=");
+ if (pos)
+ vlanid = atoi(pos + 8);
+
+ if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
+ ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
+ if (ret != -1 && *acl)
+ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+ }
+
+ return ret < 0 ? -1 : 0;
+}
+
+
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
@@ -2302,6 +2843,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "RELOG", 5) == 0) {
if (wpa_debug_reopen_file() < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
} else if (os_strcmp(buf, "STATUS") == 0) {
reply_len = hostapd_ctrl_iface_status(hapd, reply,
reply_size);
@@ -2349,7 +2892,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
reply_size);
} else if (os_strcmp(buf, "ATTACH") == 0) {
- if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
+ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
+ if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7))
reply_len = -1;
} else if (os_strcmp(buf, "DETACH") == 0) {
if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
@@ -2441,7 +2987,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
reply_len = -1;
#endif /* CONFIG_HS20 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
reply_len = -1;
@@ -2451,7 +2997,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
reply_len = -1;
-#endif /* CONFIG_WNM */
+ } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) {
+ if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
+ reply_len = -1;
+#endif /* CONFIG_WNM_AP */
} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
reply_size);
@@ -2480,6 +3029,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
reply_len = -1;
+ } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
+ if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
+ buf + 23) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
+ if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
+ reply_len = -1;
} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
reply_len = -1;
@@ -2503,6 +3059,24 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_len = -1;
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
+ if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
+ if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
+ if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
+ if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
+ if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "REKEY_GTK") == 0) {
+ if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0)
+ reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -2534,6 +3108,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_size);
} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
hostapd_ctrl_iface_pmksa_flush(hapd);
+ } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
+ if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
+ reply_len = -1;
} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
reply_len = -1;
@@ -2546,9 +3123,136 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
reply_len = -1;
+ } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
+ reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
+ reply, reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size);
+ } else if (os_strcmp(buf, "TERMINATE") == 0) {
+ eloop_terminate();
+ } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
+ if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
+ if (!hostapd_ctrl_iface_acl_add_mac(
+ &hapd->conf->accept_mac,
+ &hapd->conf->num_accept_mac, buf + 19))
+ hostapd_disassoc_accept_mac(hapd);
+ else
+ reply_len = -1;
+ } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
+ hostapd_ctrl_iface_acl_del_mac(
+ &hapd->conf->accept_mac,
+ &hapd->conf->num_accept_mac, buf + 19);
+ } else if (os_strcmp(buf + 11, "SHOW") == 0) {
+ reply_len = hostapd_ctrl_iface_acl_show_mac(
+ hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac, reply, reply_size);
+ } else if (os_strcmp(buf + 11, "CLEAR") == 0) {
+ hostapd_ctrl_iface_acl_clear_list(
+ &hapd->conf->accept_mac,
+ &hapd->conf->num_accept_mac);
+ }
+ } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
+ if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
+ if (!hostapd_ctrl_iface_acl_add_mac(
+ &hapd->conf->deny_mac,
+ &hapd->conf->num_deny_mac, buf + 17))
+ hostapd_disassoc_deny_mac(hapd);
+ } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
+ hostapd_ctrl_iface_acl_del_mac(
+ &hapd->conf->deny_mac,
+ &hapd->conf->num_deny_mac, buf + 17);
+ } else if (os_strcmp(buf + 9, "SHOW") == 0) {
+ reply_len = hostapd_ctrl_iface_acl_show_mac(
+ hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, reply, reply_size);
+ } else if (os_strcmp(buf + 9, "CLEAR") == 0) {
+ hostapd_ctrl_iface_acl_clear_list(
+ &hapd->conf->deny_mac,
+ &hapd->conf->num_deny_mac);
+ }
+#ifdef CONFIG_DPP
+ } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
+ res = hostapd_dpp_qr_code(hapd, buf + 12);
+ if (res < 0) {
+ reply_len = -1;
+ } else {
+ reply_len = os_snprintf(reply, reply_size, "%d", res);
+ if (os_snprintf_error(reply_size, reply_len))
+ reply_len = -1;
+ }
+ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) {
+ res = hostapd_dpp_bootstrap_gen(hapd, buf + 18);
+ if (res < 0) {
+ reply_len = -1;
+ } else {
+ reply_len = os_snprintf(reply, reply_size, "%d", res);
+ if (os_snprintf_error(reply_size, reply_len))
+ reply_len = -1;
+ }
+ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) {
+ if (hostapd_dpp_bootstrap_remove(hapd, 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));
+ if (!uri) {
+ reply_len = -1;
+ } else {
+ reply_len = os_snprintf(reply, reply_size, "%s", uri);
+ if (os_snprintf_error(reply_size, reply_len))
+ 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);
+ } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
+ if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
+ if (hostapd_dpp_listen(hapd, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
+ 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);
+ if (res < 0) {
+ reply_len = -1;
+ } else {
+ reply_len = os_snprintf(reply, reply_size, "%d", res);
+ if (os_snprintf_error(reply_size, reply_len))
+ reply_len = -1;
+ }
+ } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
+ if (hostapd_dpp_configurator_remove(hapd, 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)
+ 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);
+ } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
+ res = hostapd_dpp_pkex_add(hapd, buf + 12);
+ if (res < 0) {
+ reply_len = -1;
+ } else {
+ reply_len = os_snprintf(reply, reply_size, "%d", res);
+ if (os_snprintf_error(reply_size, reply_len))
+ reply_len = -1;
+ }
+ } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
+ if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
+ reply_len = -1;
+#endif /* CONFIG_DPP */
+#ifdef RADIUS_SERVER
+ } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
+ if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0)
+ reply_len = -1;
+#endif /* RADIUS_SERVER */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -2999,9 +3703,10 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
struct sockaddr_storage *from,
- socklen_t fromlen)
+ socklen_t fromlen, char *input)
{
- return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen);
+ return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen,
+ input);
}
@@ -3020,6 +3725,16 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
wps_testing_dummy_cred = 0;
wps_corrupt_pkhash = 0;
#endif /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_DPP
+ dpp_test = DPP_TEST_DISABLED;
+#endif /* CONFIG_DPP */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_DPP
+ hostapd_dpp_deinit_global(interfaces);
+#endif /* CONFIG_DPP */
}
@@ -3371,7 +4086,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
reply_len = -1;
} else if (os_strcmp(buf, "ATTACH") == 0) {
if (hostapd_global_ctrl_iface_attach(interfaces, &from,
- fromlen))
+ fromlen, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
+ if (hostapd_global_ctrl_iface_attach(interfaces, &from,
+ fromlen, buf + 7))
reply_len = -1;
} else if (os_strcmp(buf, "DETACH") == 0) {
if (hostapd_global_ctrl_iface_detach(interfaces, &from,
@@ -3478,8 +4197,6 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
}
}
- dl_list_init(&interface->global_ctrl_dst);
- interface->global_ctrl_sock = -1;
os_get_random(gcookie, COOKIE_LEN);
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
@@ -3689,6 +4406,18 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
}
+static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst,
+ const char *buf)
+{
+ /* Enable Probe Request events based on explicit request.
+ * Other events are enabled by default.
+ */
+ if (str_starts(buf, RX_PROBE_REQUEST))
+ return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST);
+ return 1;
+}
+
+
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
enum wpa_msg_type type,
const char *buf, size_t len)
@@ -3723,7 +4452,8 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
idx = 0;
dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
- if (level >= dst->debug_level) {
+ if ((level >= dst->debug_level) &&
+ hostapd_ctrl_check_event_enabled(dst, buf)) {
sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send",
&dst->addr, dst->addrlen);
msg.msg_name = &dst->addr;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 4659dd1e6bcb..77a894d5e11a 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -31,7 +31,7 @@ CONFIG_DRIVER_NL80211=y
#CONFIG_LIBNL20=y
# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
-#CONFIG_LIBNL32=y
+CONFIG_LIBNL32=y
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
@@ -50,9 +50,6 @@ CONFIG_IAPP=y
# WPA2/IEEE 802.11i RSN pre-authentication
CONFIG_RSN_PREAUTH=y
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
-
# IEEE 802.11w (management frame protection)
CONFIG_IEEE80211W=y
@@ -157,6 +154,12 @@ CONFIG_IPV6=y
# IEEE 802.11ac (Very High Throughput) support
#CONFIG_IEEE80211AC=y
+# IEEE 802.11ax HE support
+# Note: This is experimental and work in progress. The definitions are still
+# subject to change and this should not be expected to interoperate with the
+# final IEEE 802.11ax version.
+#CONFIG_IEEE80211AX=y
+
# Remove debugging code that is printing out debug messages to stdout.
# This can be used to reduce the size of the hostapd considerably if debugging
# code is not needed.
@@ -166,6 +169,9 @@ CONFIG_IPV6=y
# Disabled by default.
#CONFIG_DEBUG_FILE=y
+# Send debug messages to syslog instead of stdout
+#CONFIG_DEBUG_SYSLOG=y
+
# Add support for sending all debug messages (regardless of debug verbosity)
# to the Linux kernel tracing facility. This helps debug the entire stack by
# making it easy to record everything happening from the driver up into the
@@ -256,6 +262,7 @@ CONFIG_IPV6=y
# openssl = OpenSSL (default)
# gnutls = GnuTLS
# internal = Internal TLSv1 implementation (experimental)
+# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
# none = Empty template
#CONFIG_TLS=openssl
@@ -268,6 +275,10 @@ CONFIG_IPV6=y
# can be enabled to enable use of stronger crypto algorithms.
#CONFIG_TLSV12=y
+# Select which ciphers to use by default with OpenSSL if the user does not
+# specify them.
+#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
+
# If CONFIG_TLS=internal is used, additional library and include paths are
# needed for LibTomMath. Alternatively, an integrated, minimal version of
# LibTomMath can be used. See beginning of libtommath.c for details on benefits
@@ -343,3 +354,22 @@ CONFIG_IPV6=y
# a client, from which a signature can be produced which can identify the model
# of client device like "Nexus 6P" or "iPhone 5s".
#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
+
+# Include internal line edit mode in hostapd_cli. This can be used to provide
+# limited command line editing and history support.
+#CONFIG_WPA_CLI_EDIT=y
+
+# Opportunistic Wireless Encryption (OWE)
+# Experimental implementation of draft-harkins-owe-07.txt
+#CONFIG_OWE=y
+
+# Override default value for the wpa_disable_eapol_key_retries configuration
+# parameter. See that parameter in hostapd.conf for more details.
+#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 2117d3423a1b..5caa779dd648 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
/*
* HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012-2016, Jouni Malinen
+ * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -973,7 +973,7 @@ static void usage(void)
{
printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
"database/authenticator\n"
- "Copyright (c) 2005-2016, Jouni Malinen \n"
+ "Copyright (c) 2005-2017, Jouni Malinen \n"
"\n"
"usage:\n"
"hlr_auc_gw [-hu] [-s] [-g] "
diff --git a/hostapd/hostapd.android.rc b/hostapd/hostapd.android.rc
index 83e8d8791e3c..26a87b808914 100644
--- a/hostapd/hostapd.android.rc
+++ b/hostapd/hostapd.android.rc
@@ -9,8 +9,7 @@
on post-fs-data
mkdir /data/misc/wifi/hostapd 0770 wifi wifi
-service hostapd /system/bin/hostapd \
- -e /data/misc/wifi/entropy.bin \
+service hostapd /vendor/bin/hostapd \
/data/misc/wifi/hostapd.conf
class main
user wifi
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index fa9a855a6e5f..a005217116f1 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -98,8 +98,25 @@ ssid=test
# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
# Set as needed to indicate country in which device is operating.
# This can limit available channels and transmit power.
+# These two octets are used as the first two octets of the Country String
+# (dot11CountryString)
#country_code=US
+# The third octet of the Country String (dot11CountryString)
+# This parameter is used to set the third octet of the country string.
+#
+# All environments of the current frequency band and country (default)
+#country3=0x20
+# Outdoor environment only
+#country3=0x4f
+# Indoor environment only
+#country3=0x49
+# Noncountry entity (country_code=XX)
+#country3=0x58
+# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f
+# Annex E, Table E-4 (Global operating classes)
+#country3=0x04
+
# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
# channels and transmit power levels based on the regulatory limits. The
# country_code setting must be configured with the correct country for
@@ -182,6 +199,11 @@ channel=1
#chanlist=100 104 108 112 116
#chanlist=1 6 11-13
+# Exclude DFS channels from ACS
+# This option can be used to exclude all DFS channels from the ACS channel list
+# in cases where the driver supports DFS channels.
+#acs_exclude_dfs=1
+
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
@@ -227,6 +249,19 @@ fragm_threshold=-1
#basic_rates=10 20 55 110
#basic_rates=60 120 240
+# Beacon frame TX rate configuration
+# This sets the TX rate that is used to transmit Beacon frames. If this item is
+# not included, the driver default rate (likely lowest rate) is used.
+# Legacy (CCK/OFDM rates):
+# beacon_rate=
+# HT:
+# beacon_rate=ht:
+# VHT:
+# beacon_rate=vht:
+#
+# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM).
+#beacon_rate=10
+
# Short Preamble
# This parameter can be used to enable optional use of short preamble for
# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance.
@@ -294,7 +329,7 @@ ignore_broadcast_ssid=0
# TX queue parameters (EDCF / bursting)
# tx_queue__
-# queues: data0, data1, data2, data3, after_beacon, beacon
+# queues: data0, data1, data2, data3
# (data0 is the highest priority queue)
# parameters:
# aifs: AIFS (default 2)
@@ -476,12 +511,38 @@ wmm_ac_vo_acm=0
# Beacon and Probe Response frames.
#bss_load_update_period=50
+# Channel utilization averaging period (in BUs)
+# This field is used to enable and configure channel utilization average
+# calculation with bss_load_update_period. This should be in multiples of
+# bss_load_update_period for more accurate calculation.
+#chan_util_avg_period=600
+
# Fixed BSS Load value for testing purposes
# This field can be used to configure hostapd to add a fixed BSS Load element
# into Beacon and Probe Response frames for testing purposes. The format is
# ::
#bss_load_test=12:80:20000
+# Multicast to unicast conversion
+# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and
+# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent
+# to each station separately, with the DA replaced by their own MAC address
+# rather than the group address.
+#
+# Note that this may break certain expectations of the receiver, such as the
+# ability to drop unicast IP packets received within multicast L2 frames, or the
+# ability to not send ICMP destination unreachable messages for packets received
+# in L2 multicast (which is required, but the receiver can't tell the difference
+# if this new option is enabled).
+#
+# This also doesn't implement the 802.11 DMS (directed multicast service).
+#
+#multicast_to_unicast=0
+
+# Send broadcast Deauthentication frame on AP start/stop
+# Default: 1 (enabled)
+#broadcast_deauth=1
+
##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -692,6 +753,47 @@ wmm_ac_vo_acm=0
# setting use_sta_nsts=1.
#use_sta_nsts=0
+##### IEEE 802.11ax related configuration #####################################
+
+#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+#ieee80211ax=1
+
+#he_su_beamformer: HE single user beamformer support
+# 0 = not supported (default)
+# 1 = supported
+#he_su_beamformer=1
+
+#he_su_beamformee: HE single user beamformee support
+# 0 = not supported (default)
+# 1 = supported
+#he_su_beamformee=1
+
+#he_mu_beamformer: HE multiple user beamformer support
+# 0 = not supported (default)
+# 1 = supported
+#he_mu_beamformer=1
+
+# he_bss_color: BSS color
+# 0 = no BSS color (default)
+# unsigned integer = BSS color
+#he_bss_color=0
+
+#he_default_pe_duration: The duration of PE field in an HE PPDU in us
+# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us
+#he_default_pe_duration=0
+
+#he_twt_required: Whether TWT is required
+# 0 = not required (default)
+# 1 = required
+#he_twt_required=0
+
+#he_rts_threshold: Duration of STA transmission
+# 0 = not set (default)
+# unsigned integer = duration in units of 16 us
+#he_rts_threshold=0
+
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
@@ -835,7 +937,8 @@ eap_server=0
# OpenSSL cipher string
#
# This is an OpenSSL specific configuration option for configuring the default
-# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
+# by default) is used.
# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
# on cipher suite configuration. This is applicable only if hostapd is built to
# use OpenSSL.
@@ -1088,6 +1191,8 @@ own_ip_addr=127.0.0.1
#radius_das_port=3799
#
# DAS client (the host that can send Disconnect/CoA requests) and shared secret
+# Format:
+# IP address 0.0.0.0 can be used to allow requests from any address.
#radius_das_client=192.168.1.123 shared secret here
#
# DAS Event-Timestamp time window in seconds
@@ -1134,7 +1239,10 @@ own_ip_addr=127.0.0.1
# and/or WPA2 (full IEEE 802.11i/RSN):
# bit0 = WPA
# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
-#wpa=1
+# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2.
+# In other words, for WPA3, wpa=2 is used the configuration (and
+# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
+#wpa=2
# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
@@ -1163,31 +1271,73 @@ own_ip_addr=127.0.0.1
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
# added to enable SHA256-based stronger algorithms.
+# WPA-PSK = WPA-Personal / WPA2-Personal
+# WPA-PSK-SHA256 = WPA2-Personal using SHA256
+# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
+# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
+# SAE = SAE (WPA3-Personal)
+# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
+# FT-PSK = FT with passphrase/PSK
+# FT-EAP = FT with EAP
+# FT-EAP-SHA384 = FT with EAP using SHA384
+# FT-SAE = FT with SAE
+# FILS-SHA256 = Fast Initial Link Setup with SHA256
+# FILS-SHA384 = Fast Initial Link Setup with SHA384
+# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
+# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
+# DPP = Device Provisioning Protocol
+# OSEN = Hotspot 2.0 online signup with encryption
# (dot11RSNAConfigAuthenticationSuitesTable)
#wpa_key_mgmt=WPA-PSK WPA-EAP
# Set of accepted cipher suites (encryption algorithms) for pairwise keys
# (unicast packets). This is a space separated list of algorithms:
-# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
-# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
+# TKIP = Temporal Key Integrity Protocol
+# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
+# GCMP = Galois/counter mode protocol (GCMP-128)
+# GCMP-256 = Galois/counter mode protocol with 256-bit key
# Group cipher suite (encryption algorithm for broadcast and multicast frames)
# is automatically selected based on this configuration. If only CCMP is
# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
-# TKIP will be used as the group cipher.
+# TKIP will be used as the group cipher. The optional group_cipher parameter can
+# be used to override this automatic selection.
+#
# (dot11RSNAConfigPairwiseCiphersTable)
# Pairwise cipher for WPA (v1) (default: TKIP)
#wpa_pairwise=TKIP CCMP
# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
#rsn_pairwise=CCMP
+# Optional override for automatic group cipher selection
+# This can be used to select a specific group cipher regardless of which
+# pairwise ciphers were enabled for WPA and RSN. It should be noted that
+# overriding the group cipher with an unexpected value can result in
+# interoperability issues and in general, this parameter is mainly used for
+# testing purposes.
+#group_cipher=CCMP
+
# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
# seconds. (dot11RSNAConfigGroupRekeyTime)
-#wpa_group_rekey=600
+# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the
+# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the
+# group cipher.
+#wpa_group_rekey=86400
# Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
# (dot11RSNAConfigGroupRekeyStrict)
#wpa_strict_rekey=1
+# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is
+#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount)
+# This value should only be increased when stations are constantly
+# deauthenticated during GTK rekeying with the log message
+# "group key handshake failed...".
+# You should consider to also increase wpa_pairwise_update_count then.
+# Range 1..4294967295; default: 4
+#wpa_group_update_count=4
+
# Time interval for rekeying GMK (master key used internally to generate GTKs
# (in seconds).
#wpa_gmk_rekey=86400
@@ -1196,6 +1346,36 @@ own_ip_addr=127.0.0.1
# PTK to mitigate some attacks against TKIP deficiencies.
#wpa_ptk_rekey=600
+# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way
+# Handshake are retried per 4-Way Handshake attempt.
+# (dot11RSNAConfigPairwiseUpdateCount)
+# Range 1..4294967295; default: 4
+#wpa_pairwise_update_count=4
+
+# Workaround for key reinstallation attacks
+#
+# This parameter can be used to disable retransmission of EAPOL-Key frames that
+# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This
+# is similar to setting wpa_group_update_count=1 and
+# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with
+# extended timeout on the response to avoid causing issues with stations that
+# may use aggressive power saving have very long time in replying to the
+# EAPOL-Key messages.
+#
+# This option can be used to work around key reinstallation attacks on the
+# station (supplicant) side in cases those station devices cannot be updated
+# for some reason. By removing the retransmissions the attacker cannot cause
+# key reinstallation with a delayed frame transmission. This is related to the
+# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
+# CVE-2017-13080, and CVE-2017-13081.
+#
+# This workaround might cause interoperability issues and reduced robustness of
+# key negotiation especially in environments with heavy traffic load due to the
+# number of attempts to perform the key exchange is reduced significantly. As
+# such, this workaround is disabled by default (unless overridden in build
+# configuration). To enable this, set the parameter to 1.
+#wpa_disable_eapol_key_retries=1
+
# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
# authentication and key handshake before actually associating with a new AP.
@@ -1211,12 +1391,6 @@ own_ip_addr=127.0.0.1
# one.
#rsn_preauth_interfaces=eth0
-# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
-# allowed. This is only used with RSN/WPA2.
-# 0 = disabled (default)
-# 1 = enabled
-#peerkey=1
-
# ieee80211w: Whether management frame protection (MFP) is enabled
# 0 = disabled (default)
# 1 = optional
@@ -1259,11 +1433,44 @@ own_ip_addr=127.0.0.1
# 1 = enabled
#okc=1
+# SAE password
+# This parameter can be used to set passwords for SAE. By default, the
+# wpa_passphrase value is used if this separate parameter is not used, but
+# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though
+# SAE passwords do not have such constraints. If the BSS enabled both SAE and
+# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK
+# uses the wpa_passphrase value.
+#
+# Each sae_password entry is added to a list of available passwords. This
+# 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
+# (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.
+# sae_password uses the following encoding:
+#[|mac=][|id=]
+# Examples:
+#sae_password=secret
+#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
+#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier
+
# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
# This parameter defines how many open SAE instances can be in progress at the
# same time before the anti-clogging mechanism is taken into use.
#sae_anti_clogging_threshold=5
+# Maximum number of SAE synchronization errors (dot11RSNASAESync)
+# The offending SAe peer will be disconnected if more than this many
+# synchronization errors happen.
+#sae_sync=5
+
# 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
@@ -1273,6 +1480,75 @@ own_ip_addr=127.0.0.1
# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
#sae_groups=19 20 21 25 26
+# Require MFP for all associations using SAE
+# This parameter can be used to enforce negotiation of MFP for all associations
+# that negotiate use of SAE. This is used in cases where SAE-capable devices are
+# known to be MFP-capable and the BSS is configured with optional MFP
+# (ieee80211w=1) for legacy support. The non-SAE stations can connect without
+# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1.
+#sae_require_mfp=0
+
+# FILS Cache Identifier (16-bit value in hexdump format)
+#fils_cache_id=0011
+
+# FILS Realm Information
+# One or more FILS realms need to be configured when FILS is enabled. This list
+# of realms is used to define which realms (used in keyName-NAI by the client)
+# can be used with FILS shared key authentication for ERP.
+#fils_realm=example.com
+#fils_realm=example.org
+
+# FILS DH Group for PFS
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 DH Group to use for FILS PFS
+#fils_dh_group=0
+
+# OWE DH groups
+# OWE implementations are required to support group 19 (NIST P-256). All groups
+# that are supported by the implementation (e.g., groups 19, 20, and 21 when
+# using OpenSSL) 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:
+# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
+#owe_groups=19 20 21
+
+# OWE transition mode configuration
+# Pointer to the matching open/OWE BSS
+#owe_transition_bssid=
+# SSID in same format as ssid2 described above.
+#owe_transition_ssid=
+# Alternatively, OWE transition mode BSSID/SSID can be configured with a
+# reference to a BSS operated by this hostapd process.
+#owe_transition_ifname=
+
+# DHCP server for FILS HLP
+# If configured, hostapd will act as a DHCP relay for all FILS HLP requests
+# that include a DHCPDISCOVER message and send them to the specific DHCP
+# server for processing. hostapd will then wait for a response from that server
+# before replying with (Re)Association Response frame that encapsulates this
+# DHCP response. own_ip_addr is used as the local address for the communication
+# with the DHCP server.
+#dhcp_server=127.0.0.1
+
+# DHCP server UDP port
+# Default: 67
+#dhcp_server_port=67
+
+# DHCP relay UDP port on the local device
+# Default: 67; 0 means not to bind any specific port
+#dhcp_relay_port=67
+
+# DHCP rapid commit proxy
+# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to
+# allow the rapid commit options (two message DHCP exchange) to be used with a
+# server that supports only the four message DHCP exchange. This is disabled by
+# default (= 0) and can be enabled by setting this to 1.
+#dhcp_rapid_commit_proxy=0
+
+# Wait time for FILS HLP (dot11HLPWaitTime) in TUs
+# default: 30 TUs (= 30.72 milliseconds)
+#fils_hlp_wait_time=30
+
##### IEEE 802.11r configuration ##############################################
# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
@@ -1285,9 +1561,16 @@ own_ip_addr=127.0.0.1
# 1 to 48 octet identifier.
# This is configured with nas_identifier (see RADIUS client section above).
-# Default lifetime of the PMK-RO in minutes; range 1..65535
+# Default lifetime of the PMK-R0 in seconds; range 60..4294967295
+# (default: 14 days / 1209600 seconds; 0 = disable timeout)
# (dot11FTR0KeyLifetime)
-#r0_key_lifetime=10000
+#ft_r0_key_lifetime=1209600
+
+# Maximum lifetime for PMK-R1; applied only if not zero
+# PMK-R1 is removed at latest after this limit.
+# Removing any PMK-R1 for expiry can be disabled by setting this to -1.
+# (default: 0)
+#r1_max_key_lifetime=0
# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID)
# 6-octet identifier as a hex string.
@@ -1299,22 +1582,52 @@ own_ip_addr=127.0.0.1
#reassociation_deadline=1000
# List of R0KHs in the same Mobility Domain
-# format: <128-bit key as hex string>
+# format: <256-bit key as hex string>
# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
# address when requesting PMK-R1 key from the R0KH that the STA used during the
# Initial Mobility Domain Association.
-#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
-#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
+#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
# And so on.. One line per R0KH.
+# Wildcard entry:
+# Upon receiving a response from R0KH, it will be added to this list, so
+# subsequent requests won't be broadcast. If R0KH does not reply, it will be
+# blacklisted.
+#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
# List of R1KHs in the same Mobility Domain
-# format: <128-bit key as hex string>
+# format: <256-bit key as hex string>
# This list is used to map R1KH-ID to a destination MAC address when sending
# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
# that can request PMK-R1 keys.
-#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
-#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
+#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
+#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
# And so on.. One line per R1KH.
+# Wildcard entry:
+# Upon receiving a request from an R1KH not yet known, it will be added to this
+# list and thus will receive push notifications.
+#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
+
+# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
+# Special values: 0 -> do not expire
+# Warning: do not cache implies no sequence number validation with wildcards
+#rkh_pos_timeout=86400 (default = 1 day)
+
+# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request
+# and number of retries.
+#rkh_pull_timeout=1000 (default = 1 second)
+#rkh_pull_retries=4 (default)
+
+# Timeout (seconds) for non replying R0KH (see wildcard entries above)
+# Special values: 0 -> do not cache
+# default: 60 seconds
+#rkh_neg_timeout=60
+
+# Note: The R0KH/R1KH keys used to be 128-bit in length before the message
+# format was changed. That shorter key length is still supported for backwards
+# compatibility of the configuration files. If such a shorter key is used, a
+# 256-bit key is derived from it. For new deployments, configuring the 256-bit
+# key is recommended.
# Whether PMK-R1 push is enabled at R0KH
# 0 = do not push PMK-R1 to all configured R1KHs (default)
@@ -1326,6 +1639,14 @@ own_ip_addr=127.0.0.1
# 1 = FT-over-DS enabled (default)
#ft_over_ds=1
+# Whether to generate FT response locally for PSK networks
+# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as
+# the required information (PSK and other session data) is already locally
+# available.
+# 0 = disabled (default)
+# 1 = enabled
+#ft_psk_generate_local=0
+
##### Neighbor table ##########################################################
# Maximum number of entries kept in AP table (either for neigbor table or for
# detecting Overlapping Legacy BSS Condition). The oldest entry will be
@@ -1596,6 +1917,18 @@ own_ip_addr=127.0.0.1
# 1 = enabled (allow stations to use WNM-Sleep Mode)
#wnm_sleep_mode=1
+# WNM-Sleep Mode GTK/IGTK workaround
+# Normally, WNM-Sleep Mode exit with management frame protection negotiated
+# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode
+# Response frame. Some station implementations may have a vulnerability that
+# results in GTK/IGTK reinstallation based on this frame being replayed. This
+# configuration parameter can be used to disable that behavior and use EAPOL-Key
+# frames for GTK/IGTK update instead. This would likely be only used with
+# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues
+# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087
+# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1.
+#wnm_sleep_mode_no_keys=0
+
# BSS Transition Management
# 0 = disabled (default)
# 1 = enabled
@@ -1683,6 +2016,15 @@ own_ip_addr=127.0.0.1
# (double quoted string, printf-escaped string)
#venue_name=P"eng:Example\nvenue"
+# Venue URL information
+# This parameter can be used to configure one or more Venue URL Duples to
+# provide additional information corresponding to Venue Name information.
+# Each entry has a Venue Number value separated by colon from the Venue URL
+# string. Venue Number indicates the corresponding venue_name entry (1 = 1st
+# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name)
+#venue_url=1:http://www.example.com/info-eng
+#venue_url=2:http://www.example.com/info-fin
+
# Network Authentication Type
# This parameter indicates what type of network authentication is used in the
# network.
@@ -1853,7 +2195,27 @@ own_ip_addr=127.0.0.1
# channels 36-48):
#hs20_operating_class=5173
-# OSU icons
+# Terms and Conditions information
+#
+# hs20_t_c_filename contains the Terms and Conditions filename that the AP
+# indicates in RADIUS Access-Request messages.
+#hs20_t_c_filename=terms-and-conditions
+#
+# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP
+# indicates in RADIUS Access-Request messages. Usually, this contains the number
+# of seconds since January 1, 1970 00:00 UTC showing the time when the file was
+# last modified.
+#hs20_t_c_timestamp=1234567
+#
+# hs20_t_c_server_url contains a template for the Terms and Conditions server
+# URL. This template is used to generate the URL for a STA that needs to
+# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this
+# parameter is used on the authentication server, not the AP.
+# Macros:
+# @1@ = MAC address of the STA (colon separated hex octets)
+#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123
+
+# OSU and Operator icons
# :::::
#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
@@ -1865,12 +2227,15 @@ own_ip_addr=127.0.0.1
# OSU Providers
# One or more sets of following parameter. Each OSU provider is started by the
# mandatory osu_server_uri item. The other parameters add information for the
-# last added OSU provider.
+# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN
+# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI
+# value for OSEN authentication when using a shared BSS (Single SSID) for OSU.
#
#osu_server_uri=https://example.com/osu/
#osu_friendly_name=eng:Example operator
#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
#osu_nai=anonymous@example.com
+#osu_nai2=anonymous@example.com
#osu_method_list=1 0
#osu_icon=icon32
#osu_icon=icon64
@@ -1879,6 +2244,35 @@ own_ip_addr=127.0.0.1
#
#osu_server_uri=...
+# Operator Icons
+# Operator icons are specified using references to the hs20_icon entries
+# (Name subfield). This information, if present, is advertsised in the
+# Operator Icon Metadata ANQO-element.
+#operator_icon=icon32
+#operator_icon=icon64
+
+##### Multiband Operation (MBO) ###############################################
+#
+# MBO enabled
+# 0 = disabled (default)
+# 1 = enabled
+#mbo=1
+#
+# Cellular data connection preference
+# 0 = Excluded - AP does not want STA to use the cellular data connection
+# 1 = AP prefers the STA not to use cellular data connection
+# 255 = AP prefers the STA to use cellular data connection
+#mbo_cell_data_conn_pref=1
+
+##### Optimized Connectivity Experience (OCE) #################################
+#
+# Enable OCE specific features (bitmap)
+# BIT(0) - Reserved
+# Set BIT(1) (= 2) to enable OCE in STA-CFON mode
+# Set BIT(2) (= 4) to enable OCE in AP mode
+# Default is 0 = OCE disabled
+#oce=0
+
##### Fast Session Transfer (FST) support #####################################
#
# The options in this section are only available when the build configuration
@@ -1916,6 +2310,9 @@ own_ip_addr=127.0.0.1
# Enable neighbor report via radio measurements
#rrm_neighbor_report=1
+# Enable beacon report via radio measurements
+#rrm_beacon_report=1
+
# Publish fine timing measurement (FTM) responder functionality
# This parameter only controls publishing via Extended Capabilities element.
# Actual functionality is managed outside hostapd.
@@ -1925,6 +2322,12 @@ own_ip_addr=127.0.0.1
# This parameter only controls publishing via Extended Capabilities element.
# Actual functionality is managed outside hostapd.
#ftm_initiator=0
+#
+# Stationary AP config indicates that the AP doesn't move hence location data
+# can be considered as always up to date. If configured, LCI data will be sent
+# as a radio measurement even if the request doesn't contain a max age element
+# that allows sending of such data. Default: 0.
+#stationary_ap=0
##### TESTING OPTIONS #########################################################
#
diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite
index 826db349cfc2..411b9eafa264 100644
--- a/hostapd/hostapd.eap_user_sqlite
+++ b/hostapd/hostapd.eap_user_sqlite
@@ -3,7 +3,8 @@ CREATE TABLE users(
methods TEXT,
password TEXT,
remediation TEXT,
- phase2 INTEGER
+ phase2 INTEGER,
+ t_c_timestamp INTEGER
);
CREATE TABLE wildcards(
@@ -24,3 +25,18 @@ CREATE TABLE authlog(
username TEXT,
note TEXT
);
+
+CREATE TABLE pending_tc(
+ mac_addr TEXT PRIMARY KEY,
+ identity TEXT
+);
+
+CREATE TABLE current_sessions(
+ mac_addr TEXT PRIMARY KEY,
+ identity TEXT,
+ start_time TEXT,
+ nas TEXT,
+ hs20_t_c_filtering BOOLEAN,
+ waiting_coa_ack BOOLEAN,
+ coa_ack_received BOOLEAN
+);
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 5e6254244b31..489da397c63d 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-2016, Jouni Malinen
+ * Copyright (c) 2004-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,7 +21,7 @@
static const char *const hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2016, Jouni Malinen and contributors";
+"Copyright (c) 2004-2018, Jouni Malinen and contributors";
static struct wpa_ctrl *ctrl_conn;
static int hostapd_cli_quit = 0;
@@ -45,6 +45,8 @@ static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
static void print_help(FILE *stream, const char *cmd);
static char ** list_cmd_list(void);
static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx);
+static void update_stations(struct wpa_ctrl *ctrl);
+static void cli_event(const char *str);
static void usage(void)
@@ -147,13 +149,45 @@ static void hostapd_cli_close_connection(void)
}
+static int hostapd_cli_reconnect(const char *ifname)
+{
+ char *next_ctrl_ifname;
+
+ hostapd_cli_close_connection();
+
+ if (!ifname)
+ return -1;
+
+ next_ctrl_ifname = os_strdup(ifname);
+ os_free(ctrl_ifname);
+ ctrl_ifname = next_ctrl_ifname;
+ if (!ctrl_ifname)
+ return -1;
+
+ ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+ if (!ctrl_conn)
+ return -1;
+ if (!interactive && !action_file)
+ return 0;
+ if (wpa_ctrl_attach(ctrl_conn) == 0) {
+ hostapd_cli_attached = 1;
+ register_event_handler(ctrl_conn);
+ update_stations(ctrl_conn);
+ } else {
+ printf("Warning: Failed to attach to hostapd.\n");
+ }
+ return 0;
+}
+
+
static void hostapd_cli_msg_cb(char *msg, size_t len)
{
+ cli_event(msg);
printf("%s\n", msg);
}
-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
{
char buf[4096];
size_t len;
@@ -181,7 +215,7 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
}
-static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
{
return _wpa_ctrl_command(ctrl, cmd, 1);
}
@@ -286,6 +320,21 @@ static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static char ** hostapd_complete_stations(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&stations);
+ break;
+ }
+
+ return res;
+}
+
+
static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -318,21 +367,6 @@ static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
}
-static char ** hostapd_complete_deauthenticate(const char *str, int pos)
-{
- int arg = get_cmd_arg_num(str, pos);
- char **res = NULL;
-
- switch (arg) {
- case 1:
- res = cli_txt_list_array(&stations);
- break;
- }
-
- return res;
-}
-
-
static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -351,21 +385,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
}
-static char ** hostapd_complete_disassociate(const char *str, int pos)
-{
- int arg = get_cmd_arg_num(str, pos);
- char **res = NULL;
-
- switch (arg) {
- case 1:
- res = cli_txt_list_array(&stations);
- break;
- }
-
- return res;
-}
-
-
#ifdef CONFIG_TAXONOMY
static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
char *argv[])
@@ -701,8 +720,8 @@ static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
}
-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
- char *addr, size_t addr_len)
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+ char *addr, size_t addr_len, int print)
{
char buf[4096], *pos;
size_t len;
@@ -726,7 +745,8 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
buf[len] = '\0';
if (memcmp(buf, "FAIL", 4) == 0)
return -1;
- printf("%s", buf);
+ if (print)
+ printf("%s", buf);
pos = buf;
while (*pos != '\0' && *pos != '\n')
@@ -742,16 +762,33 @@ static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
{
char addr[32], cmd[64];
- if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
return 0;
do {
snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
- } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
return -1;
}
+static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char addr[32], cmd[64];
+
+ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+ return 0;
+ do {
+ if (os_strcmp(addr, "") != 0)
+ printf("%s\n", addr);
+ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+
+ return 0;
+}
+
+
static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
print_help(stdout, argc > 0 ? argv[0] : NULL);
@@ -888,6 +925,25 @@ static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static void update_stations(struct wpa_ctrl *ctrl)
+{
+ char addr[32], cmd[64];
+
+ if (!ctrl || !interactive)
+ return;
+
+ cli_txt_list_flush(&stations);
+
+ if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+ return;
+ do {
+ if (os_strcmp(addr, "") != 0)
+ cli_txt_list_add(&stations, addr);
+ os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+ } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+}
+
+
static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl,
struct dl_list *interfaces)
{
@@ -940,23 +996,7 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
hostapd_cli_list_interfaces(ctrl);
return 0;
}
-
- hostapd_cli_close_connection();
- os_free(ctrl_ifname);
- ctrl_ifname = os_strdup(argv[0]);
- if (ctrl_ifname == NULL)
- return -1;
-
- if (hostapd_cli_open_connection(ctrl_ifname)) {
- printf("Connected to interface '%s.\n", ctrl_ifname);
- if (wpa_ctrl_attach(ctrl_conn) == 0) {
- hostapd_cli_attached = 1;
- register_event_handler(ctrl_conn);
- } else {
- printf("Warning: Failed to attach to "
- "hostapd.\n");
- }
- } else {
+ if (hostapd_cli_reconnect(argv[0]) != 0) {
printf("Could not connect to interface '%s' - re-trying\n",
ctrl_ifname);
}
@@ -984,7 +1024,7 @@ static char ** hostapd_complete_interface(const char *str, int pos)
static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char cmd[256];
+ char cmd[2048];
int res;
if (argc != 2) {
@@ -1002,6 +1042,44 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static char ** hostapd_complete_set(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ const char *fields[] = {
+#ifdef CONFIG_WPS_TESTING
+ "wps_version_number", "wps_testing_dummy_cred",
+ "wps_corrupt_pkhash",
+#endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_INTERWORKING
+ "gas_frag_limit",
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_TESTING_OPTIONS
+ "ext_mgmt_frame_handling", "ext_eapol_frame_io",
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_MBO
+ "mbo_assoc_disallow",
+#endif /* CONFIG_MBO */
+ "deny_mac_file", "accept_mac_file",
+ };
+ int i, num_fields = ARRAY_SIZE(fields);
+
+ if (arg == 1) {
+ char **res;
+
+ res = os_calloc(num_fields + 1, sizeof(char *));
+ if (!res)
+ return NULL;
+ for (i = 0; i < num_fields; i++) {
+ res[i] = os_strdup(fields[i]);
+ if (!res[i])
+ return res;
+ }
+ return res;
+ }
+ return NULL;
+}
+
+
static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
@@ -1022,6 +1100,31 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static char ** hostapd_complete_get(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ const char *fields[] = {
+ "version", "tls_library",
+ };
+ int i, num_fields = ARRAY_SIZE(fields);
+
+ if (arg == 1) {
+ char **res;
+
+ res = os_calloc(num_fields + 1, sizeof(char *));
+ if (!res)
+ return NULL;
+ for (i = 0; i < num_fields; i++) {
+ res[i] = os_strdup(fields[i]);
+ if (!res[i])
+ return res;
+ }
+ return res;
+ }
+ return NULL;
+}
+
+
#ifdef CONFIG_FST
static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
@@ -1185,14 +1288,14 @@ static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc,
char cmd[2048];
int res;
- if (argc < 3 || argc > 5) {
- printf("Invalid set_neighbor command: needs 3-5 arguments\n");
+ if (argc < 3 || argc > 6) {
+ printf("Invalid set_neighbor command: needs 3-6 arguments\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s",
+ res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s",
argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "",
- argc == 5 ? argv[4] : "");
+ argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : "");
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long SET_NEIGHBOR command.\n");
return -1;
@@ -1261,6 +1364,122 @@ static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
}
+#ifdef CONFIG_DPP
+
+static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_DPP */
+
+
+static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv);
+}
+
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1273,26 +1492,30 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
"= pings hostapd" },
{ "mib", hostapd_cli_cmd_mib, NULL,
"= get MIB variables (dot1x, dot11, radius)" },
- { "relog", hostapd_cli_cmd_relog, NULL, NULL },
- { "status", hostapd_cli_cmd_status, NULL, NULL },
- { "sta", hostapd_cli_cmd_sta, NULL,
+ { "relog", hostapd_cli_cmd_relog, NULL,
+ "= reload/truncate debug log output file" },
+ { "status", hostapd_cli_cmd_status, NULL,
+ "= show interface status info" },
+ { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
" = get MIB variables for one station" },
{ "all_sta", hostapd_cli_cmd_all_sta, NULL,
"= get MIB variables for all stations" },
+ { "list_sta", hostapd_cli_cmd_list_sta, NULL,
+ "= list all stations" },
{ "new_sta", hostapd_cli_cmd_new_sta, NULL,
" = add a new station" },
{ "deauthenticate", hostapd_cli_cmd_deauthenticate,
- hostapd_complete_deauthenticate,
+ hostapd_complete_stations,
" = deauthenticate a station" },
{ "disassociate", hostapd_cli_cmd_disassociate,
- hostapd_complete_disassociate,
+ hostapd_complete_stations,
" = disassociate a station" },
#ifdef CONFIG_TAXONOMY
- { "signature", hostapd_cli_cmd_signature, NULL,
+ { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
" = get taxonomy signature for a station" },
#endif /* CONFIG_TAXONOMY */
#ifdef CONFIG_IEEE80211W
- { "sa_query", hostapd_cli_cmd_sa_query, NULL,
+ { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
" = send SA Query to a station" },
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
@@ -1321,9 +1544,12 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
"= show current WPS status" },
#endif /* CONFIG_WPS */
- { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, NULL },
- { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, NULL },
- { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, NULL },
+ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
+ "= send Disassociation Imminent notification" },
+ { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
+ "= send ESS Dissassociation Imminent notification" },
+ { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL,
+ "= send BSS Transition Management Request" },
{ "get_config", hostapd_cli_cmd_get_config, NULL,
"= show current configuration" },
{ "help", hostapd_cli_cmd_help, hostapd_cli_complete_help,
@@ -1331,35 +1557,100 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "interface", hostapd_cli_cmd_interface, hostapd_complete_interface,
"[ifname] = show interfaces/select interface" },
#ifdef CONFIG_FST
- { "fst", hostapd_cli_cmd_fst, NULL, NULL },
+ { "fst", hostapd_cli_cmd_fst, NULL,
+ " = send FST-MANAGER control interface command" },
#endif /* CONFIG_FST */
- { "raw", hostapd_cli_cmd_raw, NULL, NULL },
+ { "raw", hostapd_cli_cmd_raw, NULL,
+ " = send unprocessed command" },
{ "level", hostapd_cli_cmd_level, NULL,
" = change debug level" },
{ "license", hostapd_cli_cmd_license, NULL,
"= show full hostapd_cli license" },
{ "quit", hostapd_cli_cmd_quit, NULL,
"= exit hostapd_cli" },
- { "set", hostapd_cli_cmd_set, NULL, NULL },
- { "get", hostapd_cli_cmd_get, NULL, NULL },
- { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, NULL },
- { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, NULL, NULL },
- { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, NULL },
- { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, NULL },
- { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, NULL },
- { "vendor", hostapd_cli_cmd_vendor, NULL, NULL },
- { "enable", hostapd_cli_cmd_enable, NULL, NULL },
- { "reload", hostapd_cli_cmd_reload, NULL, NULL },
- { "disable", hostapd_cli_cmd_disable, NULL, NULL },
- { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, NULL },
- { "log_level", hostapd_cli_cmd_log_level, NULL, NULL },
- { "pmksa", hostapd_cli_cmd_pmksa, NULL, NULL },
- { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, NULL },
- { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, NULL },
- { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, NULL },
- { "req_lci", hostapd_cli_cmd_req_lci, NULL, NULL },
- { "req_range", hostapd_cli_cmd_req_range, NULL, NULL },
- { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, NULL },
+ { "set", hostapd_cli_cmd_set, hostapd_complete_set,
+ " = set runtime variables" },
+ { "get", hostapd_cli_cmd_get, hostapd_complete_get,
+ " = get runtime info" },
+ { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL,
+ " = set QoS Map set element" },
+ { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf,
+ hostapd_complete_stations,
+ " = send QoS Map Configure frame" },
+ { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+ " [sec_channel_offset=] [center_freq1=]\n"
+ " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
+ " = initiate channel switch announcement" },
+ { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
+ " \n"
+ " = send WNM-Notification Subscription Remediation Request" },
+ { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL,
+ " [url]\n"
+ " = send WNM-Notification imminent deauthentication indication" },
+ { "vendor", hostapd_cli_cmd_vendor, NULL,
+ " []\n"
+ " = send vendor driver command" },
+ { "enable", hostapd_cli_cmd_enable, NULL,
+ "= enable hostapd on current interface" },
+ { "reload", hostapd_cli_cmd_reload, NULL,
+ "= reload configuration for current interface" },
+ { "disable", hostapd_cli_cmd_disable, NULL,
+ "= disable hostapd on current interface" },
+ { "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+ "= drop all ERP keys"},
+ { "log_level", hostapd_cli_cmd_log_level, NULL,
+ "[level] = show/change log verbosity level" },
+ { "pmksa", hostapd_cli_cmd_pmksa, NULL,
+ " = show PMKSA cache entries" },
+ { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL,
+ " = flush PMKSA cache" },
+ { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
+ " [lci=] [civic=] [stat]\n"
+ " = add AP to neighbor database" },
+ { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
+ " = remove AP from neighbor database" },
+ { "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
+ " = send LCI request to a station"},
+ { "req_range", hostapd_cli_cmd_req_range, NULL,
+ " = send FTM range request"},
+ { "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
+ " = show supported driver flags"},
+#ifdef CONFIG_DPP
+ { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
+ "report a scanned DPP URI from a QR Code" },
+ { "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
+ "type= [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+ { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
+ "*| = remove DPP bootstrap information" },
+ { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
+ " = get DPP bootstrap URI" },
+ { "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
+ " = show DPP bootstrap information" },
+ { "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
+ "peer= [own=] = initiate DPP bootstrapping" },
+ { "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
+ " = start DPP listen" },
+ { "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL,
+ "= stop DPP listen" },
+ { "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL,
+ "[curve=..] [key=..] = add DPP configurator" },
+ { "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove,
+ NULL,
+ "*| = remove DPP configurator" },
+ { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key,
+ NULL,
+ " = Get DPP configurator's private key" },
+ { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL,
+ "add PKEX code" },
+ { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
+ "*| = remove DPP pkex information" },
+#endif /* CONFIG_DPP */
+ { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
+ "=Add/Delete/Show/Clear accept MAC ACL" },
+ { "deny_acl", hostapd_cli_cmd_deny_macacl, NULL,
+ "=Add/Delete/Show/Clear deny MAC ACL" },
+ { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations,
+ " = poll a STA to check connectivity with a QoS null frame" },
{ NULL, NULL, NULL, NULL }
};
@@ -1471,7 +1762,7 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
if (ctrl_conn == NULL)
return;
while (wpa_ctrl_pending(ctrl)) {
- char buf[256];
+ char buf[4096];
size_t len = sizeof(buf) - 1;
if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
buf[len] = '\0';
@@ -1504,19 +1795,8 @@ static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
printf("Connection to hostapd lost - trying to reconnect\n");
hostapd_cli_close_connection();
}
- if (!ctrl_conn) {
- ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
- if (ctrl_conn) {
- printf("Connection to hostapd re-established\n");
- if (wpa_ctrl_attach(ctrl_conn) == 0) {
- hostapd_cli_attached = 1;
- register_event_handler(ctrl_conn);
- } else {
- printf("Warning: Failed to attach to "
- "hostapd.\n");
- }
- }
- }
+ if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0)
+ printf("Connection to hostapd re-established\n");
if (ctrl_conn)
hostapd_cli_recv_pending(ctrl_conn, 1, 0);
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
@@ -1611,17 +1891,34 @@ static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str,
static void hostapd_cli_interactive(void)
{
+ char *hfile = NULL;
+ char *home;
+
printf("\nInteractive mode\n\n");
+#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR
+ home = CONFIG_HOSTAPD_CLI_HISTORY_DIR;
+#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
+ home = getenv("HOME");
+#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
+ if (home) {
+ const char *fname = ".hostapd_cli_history";
+ int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+ hfile = os_malloc(hfile_len);
+ if (hfile)
+ os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+ }
+
eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
- hostapd_cli_edit_completion_cb, NULL, NULL, NULL);
+ hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
eloop_run();
cli_txt_list_flush(&stations);
- edit_deinit(NULL, NULL);
+ edit_deinit(hfile, NULL);
+ os_free(hfile);
eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
}
@@ -1748,7 +2045,7 @@ int main(int argc, char *argv[])
closedir(dir);
}
}
- ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+ hostapd_cli_reconnect(ctrl_ifname);
if (ctrl_conn) {
if (warning_displayed)
printf("Connection established.\n");
@@ -1769,17 +2066,8 @@ int main(int argc, char *argv[])
continue;
}
- if (interactive || action_file) {
- if (wpa_ctrl_attach(ctrl_conn) == 0) {
- hostapd_cli_attached = 1;
- register_event_handler(ctrl_conn);
- } else {
- printf("Warning: Failed to attach to hostapd.\n");
- if (action_file)
- return -1;
- }
- }
-
+ if (action_file && !hostapd_cli_attached)
+ return -1;
if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
return -1;
diff --git a/hostapd/main.c b/hostapd/main.c
index 2c8dbd30a274..414dfe4241e0 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -1,6 +1,6 @@
/*
* hostapd / main()
- * Copyright (c) 2002-2016, Jouni Malinen
+ * Copyright (c) 2002-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -24,6 +24,7 @@
#include "ap/hostapd.h"
#include "ap/ap_config.h"
#include "ap/ap_drv_ops.h"
+#include "ap/dpp_hostapd.h"
#include "fst/fst.h"
#include "config_file.h"
#include "eap_register.h"
@@ -108,6 +109,10 @@ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
module_str ? module_str : "",
module_str ? ": " : "", txt);
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog)
+ conf_stdout = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
if ((conf_stdout & module) && level >= conf_stdout_level) {
wpa_debug_print_timestamp();
wpa_printf(MSG_INFO, "%s", format);
@@ -248,7 +253,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
*
* This function is used to parse configuration file for a full interface (one
* or more BSSes sharing the same radio) and allocate memory for the BSS
- * interfaces. No actiual driver operations are started.
+ * interfaces. No actual driver operations are started.
*/
static struct hostapd_iface *
hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
@@ -451,7 +456,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-2016, Jouni Malinen "
+ "Copyright (c) 2002-2018, Jouni Malinen "
"and contributors\n");
}
@@ -480,10 +485,13 @@ static void usage(void)
" -f log output to debug file instead of stdout\n"
#endif /* CONFIG_DEBUG_FILE */
#ifdef CONFIG_DEBUG_LINUX_TRACING
- " -T = record to Linux tracing in addition to logging\n"
+ " -T record to Linux tracing in addition to logging\n"
" (records all messages regardless of debug verbosity)\n"
#endif /* CONFIG_DEBUG_LINUX_TRACING */
" -i list of interface names to use\n"
+#ifdef CONFIG_DEBUG_SYSLOG
+ " -s log output to syslog instead of stdout\n"
+#endif /* CONFIG_DEBUG_SYSLOG */
" -S start all the interfaces synchronously\n"
" -t include timestamps in some debug messages\n"
" -v show hostapd version\n");
@@ -549,14 +557,14 @@ static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
static int hostapd_get_interface_names(char ***if_names,
size_t *if_names_size,
- char *optarg)
+ char *arg)
{
char *if_name, *tmp, **nnames;
size_t i;
- if (!optarg)
+ if (!arg)
return -1;
- if_name = strtok_r(optarg, ",", &tmp);
+ if_name = strtok_r(arg, ",", &tmp);
while (if_name) {
nnames = os_realloc_array(*if_names, 1 + *if_names_size,
@@ -659,9 +667,15 @@ int main(int argc, char *argv[])
interfaces.global_iface_name = NULL;
interfaces.global_ctrl_sock = -1;
dl_list_init(&interfaces.global_ctrl_dst);
+#ifdef CONFIG_ETH_P_OUI
+ dl_list_init(&interfaces.eth_p_oui);
+#endif /* CONFIG_ETH_P_OUI */
+#ifdef CONFIG_DPP
+ hostapd_dpp_init_global(&interfaces);
+#endif /* CONFIG_DPP */
for (;;) {
- c = getopt(argc, argv, "b:Bde:f:hi:KP:STtu:vg:G:");
+ c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
if (c < 0)
break;
switch (c) {
@@ -718,6 +732,11 @@ int main(int argc, char *argv[])
bss_config = tmp_bss;
bss_config[num_bss_configs++] = optarg;
break;
+#ifdef CONFIG_DEBUG_SYSLOG
+ case 's':
+ wpa_debug_syslog = 1;
+ break;
+#endif /* CONFIG_DEBUG_SYSLOG */
case 'S':
start_ifaces_in_sync = 1;
break;
@@ -746,6 +765,10 @@ int main(int argc, char *argv[])
wpa_debug_open_file(log_file);
else
wpa_debug_setup_stdout();
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog)
+ wpa_debug_open_syslog();
+#endif /* CONFIG_DEBUG_SYSLOG */
#ifdef CONFIG_DEBUG_LINUX_TRACING
if (enable_trace_dbg) {
int tret = wpa_debug_open_linux_tracing();
@@ -877,11 +900,16 @@ int main(int argc, char *argv[])
}
os_free(interfaces.iface);
+#ifdef CONFIG_DPP
+ hostapd_dpp_deinit_global(&interfaces);
+#endif /* CONFIG_DPP */
+
if (interfaces.eloop_initialized)
eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
os_free(pid_file);
+ wpa_debug_close_syslog();
if (log_file)
wpa_debug_close_file();
wpa_debug_close_linux_tracing();
diff --git a/hs20/client/est.c b/hs20/client/est.c
index 9f1519bf4e4e..b1aacb8ffbbe 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -666,7 +666,6 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
char *buf, *resp, *req, *req2;
size_t buflen, resp_len, len, pkcs7_len;
unsigned char *pkcs7;
- FILE *f;
char client_cert_buf[200];
char client_key_buf[200];
const char *client_cert = NULL, *client_key = NULL;
@@ -721,11 +720,6 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
return -1;
}
wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
- f = fopen("Cert/est-resp.raw", "w");
- if (f) {
- fwrite(resp, resp_len, 1, f);
- fclose(f);
- }
pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
if (pkcs7 == NULL) {
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
index 5854b726dba2..d75c84562aca 100644
--- a/hs20/client/oma_dm_client.c
+++ b/hs20/client/oma_dm_client.c
@@ -111,6 +111,12 @@ static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
xml_node_t *syncml, *synchdr;
xml_namespace_t *ns;
+ if (!ctx->devid) {
+ wpa_printf(MSG_ERROR,
+ "DevId from devinfo.xml is not available - cannot use OMA DM");
+ return NULL;
+ }
+
syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
"SyncML");
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index c05c57d44f89..636e10666f8b 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -105,6 +105,35 @@ static int valid_fqdn(const char *fqdn)
}
+static int android_update_permission(const char *path, mode_t mode)
+{
+#ifdef ANDROID
+ /* we need to change file/folder permission for Android */
+
+ if (!path) {
+ wpa_printf(MSG_ERROR, "file path null");
+ return -1;
+ }
+
+ /* Allow processes running with Group ID as AID_WIFI,
+ * to read files from SP, SP/, Cert and osu-info directories */
+ if (chown(path, -1, AID_WIFI)) {
+ wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (chmod(path, mode) < 0) {
+ wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
+ strerror(errno));
+ return -1;
+ }
+#endif /* ANDROID */
+
+ return 0;
+}
+
+
int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
{
xml_node_t *node;
@@ -169,6 +198,8 @@ int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
}
mkdir("Cert", S_IRWXU);
+ android_update_permission("Cert", S_IRWXU | S_IRWXG);
+
if (est_load_cacerts(ctx, url) < 0 ||
est_build_csr(ctx, url) < 0 ||
est_simple_enroll(ctx, url, user, pw) < 0)
@@ -262,7 +293,6 @@ static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
unlink("Cert/est-req.b64");
unlink("Cert/est-req.pem");
- unlink("Cert/est-resp.raw");
rmdir("Cert");
return 0;
@@ -406,7 +436,7 @@ static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
if (node == NULL) {
wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
xml_node_free(ctx->xml, pps);
- return -1;
+ return -2;
}
ret = download_cert(ctx, node, ca_fname);
@@ -433,7 +463,7 @@ static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
if (node == NULL) {
wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
xml_node_free(ctx->xml, pps);
- return -1;
+ return -2;
}
aaa = xml_node_first_child(ctx->xml, node);
@@ -455,7 +485,7 @@ static int download_trust_roots(struct hs20_osu_client *ctx,
{
char *dir, *pos;
char fname[300];
- int ret;
+ int ret, ret1;
dir = os_strdup(pps_fname);
if (dir == NULL)
@@ -470,9 +500,13 @@ static int download_trust_roots(struct hs20_osu_client *ctx,
snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
- cmd_dl_polupd_ca(ctx, pps_fname, fname);
+ ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname);
+ if (ret == 0 && ret1 == -1)
+ ret = -1;
snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
- cmd_dl_aaa_ca(ctx, pps_fname, fname);
+ ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname);
+ if (ret == 0 && ret1 == -1)
+ ret = -1;
os_free(dir);
@@ -578,20 +612,8 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
}
}
-#ifdef ANDROID
- /* Allow processes running with Group ID as AID_WIFI,
- * to read files from SP/ directory */
- if (chown(fname, -1, AID_WIFI)) {
- wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
- strerror(errno));
- /* Try to continue anyway */
- }
- if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) {
- wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
- strerror(errno));
- /* Try to continue anyway */
- }
-#endif /* ANDROID */
+ android_update_permission("SP", S_IRWXU | S_IRGRP | S_IXGRP);
+ android_update_permission(fname, S_IRWXU | S_IRGRP | S_IXGRP);
snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
@@ -1213,8 +1235,7 @@ static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
homeoi) < 0)
wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
} else {
- if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
- homeoi) < 0)
+ if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0)
wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
}
@@ -1289,7 +1310,9 @@ static void set_pps_cred_home_sp_roaming_consortium_oi(
if (str == NULL)
return;
wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
- /* TODO: Set to wpa_supplicant */
+ if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums",
+ str) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums");
xml_node_get_text_free(ctx->xml, str);
}
@@ -1442,10 +1465,92 @@ static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
}
+static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ int type;
+ const char *eap_method = NULL;
+
+ if (!str)
+ return;
+ wpa_printf(MSG_INFO,
+ "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str);
+ type = atoi(str);
+ switch (type) {
+ case EAP_TYPE_TLS:
+ eap_method = "TLS";
+ break;
+ case EAP_TYPE_TTLS:
+ eap_method = "TTLS";
+ break;
+ case EAP_TYPE_PEAP:
+ eap_method = "PEAP";
+ break;
+ case EAP_TYPE_PWD:
+ eap_method = "PWD";
+ break;
+ }
+ xml_node_get_text_free(ctx->xml, str);
+ if (!eap_method) {
+ wpa_printf(MSG_INFO, "Unknown EAPType value");
+ return;
+ }
+
+ if (set_cred(ctx->ifname, id, "eap", eap_method) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred eap");
+}
+
+
+static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx,
+ int id, xml_node_t *node)
+{
+ char *str = xml_node_get_text(ctx->xml, node);
+ const char *phase2 = NULL;
+
+ if (!str)
+ return;
+ wpa_printf(MSG_INFO,
+ "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s",
+ str);
+ if (os_strcmp(str, "PAP") == 0)
+ phase2 = "auth=PAP";
+ else if (os_strcmp(str, "CHAP") == 0)
+ phase2 = "auth=CHAP";
+ else if (os_strcmp(str, "MS-CHAP") == 0)
+ phase2 = "auth=MSCHAP";
+ else if (os_strcmp(str, "MS-CHAP-V2") == 0)
+ phase2 = "auth=MSCHAPV2";
+ xml_node_get_text_free(ctx->xml, str);
+ if (!phase2) {
+ wpa_printf(MSG_INFO, "Unknown InnerMethod value");
+ return;
+ }
+
+ if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0)
+ wpa_printf(MSG_INFO, "Failed to set cred phase2");
+}
+
+
static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
xml_node_t *node)
{
- wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
+ xml_node_t *child;
+ const char *name;
+
+ wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod");
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (os_strcasecmp(name, "EAPType") == 0)
+ set_pps_cred_eap_method_eap_type(ctx, id, child);
+ else if (os_strcasecmp(name, "InnerMethod") == 0)
+ set_pps_cred_eap_method_inner_method(ctx, id, child);
+ else
+ wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'",
+ name);
+ }
}
@@ -1884,7 +1989,9 @@ struct osu_data {
char url[256];
unsigned int methods;
char osu_ssid[33];
+ char osu_ssid2[33];
char osu_nai[256];
+ char osu_nai2[256];
struct osu_lang_text friendly_name[MAX_OSU_VALS];
size_t friendly_name_count;
struct osu_lang_text serv_desc[MAX_OSU_VALS];
@@ -1943,12 +2050,24 @@ static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
continue;
}
+ if (strncmp(buf, "osu_ssid2=", 10) == 0) {
+ snprintf(last->osu_ssid2, sizeof(last->osu_ssid2),
+ "%s", buf + 10);
+ continue;
+ }
+
if (os_strncmp(buf, "osu_nai=", 8) == 0) {
os_snprintf(last->osu_nai, sizeof(last->osu_nai),
"%s", buf + 8);
continue;
}
+ if (os_strncmp(buf, "osu_nai2=", 9) == 0) {
+ os_snprintf(last->osu_nai2, sizeof(last->osu_nai2),
+ "%s", buf + 9);
+ continue;
+ }
+
if (strncmp(buf, "friendly_name=", 14) == 0) {
struct osu_lang_text *txt;
if (last->friendly_name_count == MAX_OSU_VALS)
@@ -2024,9 +2143,9 @@ static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
- const char *ssid, const char *url,
+ const char *ssid, const char *ssid2, const char *url,
unsigned int methods, int no_prod_assoc,
- const char *osu_nai)
+ const char *osu_nai, const char *osu_nai2)
{
int id;
const char *ifname = ctx->ifname;
@@ -2034,26 +2153,54 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
struct wpa_ctrl *mon;
int res;
+ if (ssid2 && ssid2[0] == '\0')
+ ssid2 = NULL;
+
+ if (ctx->osu_ssid) {
+ if (os_strcmp(ssid, ctx->osu_ssid) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "Enforced OSU SSID matches ANQP info");
+ ssid2 = NULL;
+ } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "Enforced OSU SSID matches RSN[OSEN] info");
+ ssid = ssid2;
+ } else {
+ wpa_printf(MSG_INFO, "Enforced OSU SSID did not match");
+ write_summary(ctx, "Enforced OSU SSID did not match");
+ return -1;
+ }
+ }
+
id = add_network(ifname);
if (id < 0)
return -1;
if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
return -1;
+ if (ssid2)
+ osu_nai = osu_nai2;
if (osu_nai && os_strlen(osu_nai) > 0) {
char dir[255], fname[300];
if (getcwd(dir, sizeof(dir)) == NULL)
return -1;
os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+ if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0)
+ return -1;
+
if (set_network(ifname, id, "proto", "OSEN") < 0 ||
set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
set_network(ifname, id, "pairwise", "CCMP") < 0 ||
- set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
+ set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 ||
set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
set_network(ifname, id, "ocsp", "2") < 0 ||
set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
set_network_quoted(ifname, id, "ca_cert", fname) < 0)
return -1;
+ } else if (ssid2) {
+ wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]");
+ write_summary(ctx, "No OSU_NAI set for RSN[OSEN]");
+ return -1;
} else {
if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
return -1;
@@ -2134,7 +2281,7 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
char fname[255];
FILE *f;
struct osu_data *osu = NULL, *last = NULL;
- size_t osu_count, i, j;
+ size_t osu_count = 0, i, j;
int ret;
write_summary(ctx, "OSU provider selection");
@@ -2229,8 +2376,12 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
fprintf(f, "
BSSID: %s
\n"
"SSID: %s
\n",
last->bssid, last->osu_ssid);
+ if (last->osu_ssid2[0])
+ fprintf(f, "SSID2: %s
\n", last->osu_ssid2);
if (last->osu_nai[0])
fprintf(f, "NAI: %s
\n", last->osu_nai);
+ if (last->osu_nai2[0])
+ fprintf(f, "NAI2: %s
\n", last->osu_nai2);
fprintf(f, "URL: %s
\n"
"methods:%s%s
\n"
"
\n",
@@ -2257,6 +2408,8 @@ selected:
ret = 0;
wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
+ if (last->osu_ssid2[0])
+ wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2);
wpa_printf(MSG_INFO, "URL: %s", last->url);
write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
ret, last->bssid, last->osu_ssid, last->url);
@@ -2311,10 +2464,13 @@ selected:
"No supported OSU provisioning method");
ret = -1;
}
- } else if (connect)
+ } else if (connect) {
ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+ last->osu_ssid2,
last->url, last->methods,
- no_prod_assoc, last->osu_nai);
+ no_prod_assoc, last->osu_nai,
+ last->osu_nai2);
+ }
} else
ret = -1;
@@ -2346,15 +2502,7 @@ static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
return -1;
}
-#ifdef ANDROID
- /* Allow processes running with Group ID as AID_WIFI
- * to read/write files from osu-info directory
- */
- if (chown(fname, -1, AID_WIFI)) {
- wpa_printf(MSG_INFO, "Could not chown osu-info directory: %s",
- strerror(errno));
- }
-#endif /* ANDROID */
+ android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
if (wpa_command(ifname, buf) < 0) {
@@ -2920,24 +3068,17 @@ static int init_ctx(struct hs20_osu_client *ctx)
return -1;
devinfo = node_from_file(ctx->xml, "devinfo.xml");
- if (!devinfo) {
- wpa_printf(MSG_ERROR, "devinfo.xml not found");
- return -1;
- }
+ if (devinfo) {
+ devid = get_node(ctx->xml, devinfo, "DevId");
+ if (devid) {
+ char *tmp = xml_node_get_text(ctx->xml, devid);
- devid = get_node(ctx->xml, devinfo, "DevId");
- if (devid) {
- char *tmp = xml_node_get_text(ctx->xml, devid);
- if (tmp) {
- ctx->devid = os_strdup(tmp);
- xml_node_get_text_free(ctx->xml, tmp);
+ if (tmp) {
+ ctx->devid = os_strdup(tmp);
+ xml_node_get_text_free(ctx->xml, tmp);
+ }
}
- }
- xml_node_free(ctx->xml, devinfo);
-
- if (ctx->devid == NULL) {
- wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
- return -1;
+ xml_node_free(ctx->xml, devinfo);
}
ctx->http = http_init_ctx(ctx, ctx->xml);
@@ -3040,7 +3181,7 @@ int main(int argc, char *argv[])
return -1;
for (;;) {
- c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:");
+ c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:");
if (c < 0)
break;
switch (c) {
@@ -3057,6 +3198,9 @@ int main(int argc, char *argv[])
case 'N':
no_prod_assoc = 1;
break;
+ case 'o':
+ ctx.osu_ssid = optarg;
+ break;
case 'O':
friendly_name = optarg;
break;
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
index 9a7059edfddb..5c8e6d00b6bb 100644
--- a/hs20/client/osu_client.h
+++ b/hs20/client/osu_client.h
@@ -47,6 +47,7 @@ struct hs20_osu_client {
int client_cert_present;
char **server_dnsname;
size_t server_dnsname_count;
+ const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
unsigned long int workarounds;
};
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 98788fef797e..9b07ee163419 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -10,12 +10,15 @@ include ../lib.rules
CFLAGS += -DHOSTAPD
CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_ETH_P_OUI
CFLAGS += -DCONFIG_HS20
CFLAGS += -DCONFIG_INTERWORKING
CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R_AP
CFLAGS += -DCONFIG_IEEE80211W
CFLAGS += -DCONFIG_WPS
CFLAGS += -DCONFIG_PROXYARP
+CFLAGS += -DCONFIG_IPV6
CFLAGS += -DCONFIG_IAPP
LIB_OBJS= \
@@ -32,6 +35,7 @@ LIB_OBJS= \
dhcp_snoop.o \
drv_callbacks.o \
eap_user_db.o \
+ eth_p_oui.o \
gas_serv.o \
hostapd.o \
hs20.o \
@@ -43,14 +47,17 @@ LIB_OBJS= \
ieee802_11_shared.o \
ieee802_11_vht.o \
ieee802_1x.o \
+ neighbor_db.o \
ndisc_snoop.o \
p2p_hostapd.o \
- peerkey_auth.o \
pmksa_cache_auth.o \
preauth_auth.o \
+ rrm.o \
sta_info.o \
tkip_countermeasures.o \
utils.o \
+ vlan.o \
+ vlan_ifconfig.o \
vlan_init.o \
wmm.o \
wnm_ap.o \
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 5e8380535854..6d4c0416dd42 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -260,7 +260,7 @@ static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
}
-static void acs_cleanup(struct hostapd_iface *iface)
+void acs_cleanup(struct hostapd_iface *iface)
{
int i;
struct hostapd_channel_data *chan;
@@ -314,7 +314,7 @@ acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
/* TODO: figure out the best multiplier for noise floor base */
factor = pow(10, survey->nf / 5.0L) +
- (busy / total) *
+ (total ? (busy / total) : 0) *
pow(2, pow(10, (long double) survey->nf / 10.0L) -
pow(10, (long double) min_nf / 10.0L));
@@ -331,10 +331,8 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface,
long double int_factor = 0;
unsigned count = 0;
- if (dl_list_empty(&chan->survey_list))
- return;
-
- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ if (dl_list_empty(&chan->survey_list) ||
+ (chan->flag & HOSTAPD_CHAN_DISABLED))
return;
chan->interference_factor = 0;
@@ -359,9 +357,8 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface,
(unsigned long) survey->channel_time_rx);
}
- if (!count)
- return;
- chan->interference_factor /= count;
+ if (count)
+ chan->interference_factor /= count;
}
@@ -450,13 +447,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
for (i = 0; i < iface->current_mode->num_channels; i++) {
chan = &iface->current_mode->channels[i];
- if (chan->flag & HOSTAPD_CHAN_DISABLED)
- continue;
-
- if (!acs_survey_list_is_sufficient(chan))
- continue;
-
- valid++;
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ acs_survey_list_is_sufficient(chan))
+ valid++;
}
/* We need at least survey data for one channel */
@@ -466,13 +459,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
static int acs_usable_chan(struct hostapd_channel_data *chan)
{
- if (dl_list_empty(&chan->survey_list))
- return 0;
- if (chan->flag & HOSTAPD_CHAN_DISABLED)
- return 0;
- if (!acs_survey_list_is_sufficient(chan))
- return 0;
- return 1;
+ return !dl_list_empty(&chan->survey_list) &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ acs_survey_list_is_sufficient(chan);
}
@@ -788,10 +777,7 @@ static int acs_study_survey_based(struct hostapd_iface *iface)
static int acs_study_options(struct hostapd_iface *iface)
{
- int err;
-
- err = acs_study_survey_based(iface);
- if (err == 0)
+ if (acs_study_survey_based(iface) == 0)
return 0;
/* TODO: If no surveys are available/sufficient this is a good
@@ -920,14 +906,11 @@ static int acs_request_scan(struct hostapd_iface *iface)
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
{
- int err;
-
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
wpa_printf(MSG_INFO, "ACS: Offloading to driver");
- err = hostapd_drv_do_acs(iface->bss[0]);
- if (err)
+ if (hostapd_drv_do_acs(iface->bss[0]))
return HOSTAPD_CHAN_INVALID;
return HOSTAPD_CHAN_ACS;
}
@@ -937,8 +920,7 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
acs_cleanup(iface);
- err = acs_request_scan(iface);
- if (err < 0)
+ if (acs_request_scan(iface) < 0)
return HOSTAPD_CHAN_INVALID;
hostapd_set_state(iface, HAPD_IFACE_ACS);
diff --git a/src/ap/acs.h b/src/ap/acs.h
index fc85259e85d5..ec84f0ee97f3 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -13,6 +13,7 @@
#ifdef CONFIG_ACS
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+void acs_cleanup(struct hostapd_iface *iface);
#else /* CONFIG_ACS */
@@ -22,6 +23,10 @@ static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
return HOSTAPD_CHAN_INVALID;
}
+static inline void acs_cleanup(struct hostapd_iface *iface)
+{
+}
+
#endif /* CONFIG_ACS */
#endif /* ACS_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 228de2baf946..f9b6f295927f 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -10,9 +10,11 @@
#include "utils/common.h"
#include "crypto/sha1.h"
+#include "crypto/tls.h"
#include "radius/radius_client.h"
#include "common/ieee802_11_defs.h"
#include "common/eapol_common.h"
+#include "common/dhcp.h"
#include "eap_common/eap_wsc_common.h"
#include "eap_server/eap.h"
#include "wpa_auth.h"
@@ -36,6 +38,10 @@ static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
}
+#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
+#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
+#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
+
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
{
dl_list_init(&bss->anqp_elem);
@@ -55,6 +61,10 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->wpa_group_rekey = 600;
bss->wpa_gmk_rekey = 86400;
+ bss->wpa_group_update_count = 4;
+ bss->wpa_pairwise_update_count = 4;
+ bss->wpa_disable_eapol_key_retries =
+ DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
bss->wpa_pairwise = WPA_CIPHER_TKIP;
bss->wpa_group = WPA_CIPHER_TKIP;
@@ -88,13 +98,39 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
/* Set to -1 as defaults depends on HT in setup */
bss->wmm_enabled = -1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
bss->ft_over_ds = 1;
-#endif /* CONFIG_IEEE80211R */
+ bss->rkh_pos_timeout = 86400;
+ bss->rkh_neg_timeout = 60;
+ bss->rkh_pull_timeout = 1000;
+ bss->rkh_pull_retries = 4;
+ bss->r0_key_lifetime = 1209600;
+#endif /* CONFIG_IEEE80211R_AP */
bss->radius_das_time_window = 300;
bss->sae_anti_clogging_threshold = 5;
+ bss->sae_sync = 5;
+
+ bss->gas_frag_limit = 1400;
+
+#ifdef CONFIG_FILS
+ dl_list_init(&bss->fils_realms);
+ bss->fils_hlp_wait_time = 30;
+ bss->dhcp_server_port = DHCP_SERVER_PORT;
+ bss->dhcp_relay_port = DHCP_SERVER_PORT;
+#endif /* CONFIG_FILS */
+
+ bss->broadcast_deauth = 1;
+
+#ifdef CONFIG_MBO
+ bss->mbo_cell_data_conn_pref = -1;
+#endif /* CONFIG_MBO */
+
+ /* Disable TLS v1.3 by default for now to avoid interoperability issue.
+ * 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;
}
@@ -192,6 +228,11 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->acs_num_scans = 5;
#endif /* CONFIG_ACS */
+ /* The third octet of the country string uses an ASCII space character
+ * by default to indicate that the regulations encompass all
+ * environments for the current frequency band in the country. */
+ conf->country[2] = ' ';
+
return conf;
}
@@ -329,13 +370,7 @@ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
ssid->wpa_psk->group = 1;
}
- if (ssid->wpa_psk_file) {
- if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
- &conf->ssid))
- return -1;
- }
-
- return 0;
+ return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid);
}
@@ -380,10 +415,23 @@ void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
hostapd_config_free_radius_attr(user->accept_attr);
os_free(user->identity);
bin_clear_free(user->password, user->password_len);
+ bin_clear_free(user->salt, user->salt_len);
os_free(user);
}
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user)
+{
+ struct hostapd_eap_user *prev_user;
+
+ while (user) {
+ prev_user = user;
+ user = user->next;
+ hostapd_config_free_eap_user(prev_user);
+ }
+}
+
+
static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
{
int i;
@@ -420,10 +468,38 @@ static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
}
+static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_FILS
+ struct fils_realm *realm;
+
+ while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm,
+ list))) {
+ dl_list_del(&realm->list);
+ os_free(realm);
+ }
+#endif /* CONFIG_FILS */
+}
+
+
+static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
+{
+ struct sae_password_entry *pw, *tmp;
+
+ pw = conf->sae_passwords;
+ conf->sae_passwords = NULL;
+ while (pw) {
+ tmp = pw;
+ pw = pw->next;
+ str_clear_free(tmp->password);
+ os_free(tmp->identifier);
+ os_free(tmp);
+ }
+}
+
+
void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
- struct hostapd_eap_user *user, *prev_user;
-
if (conf == NULL)
return;
@@ -436,12 +512,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->ssid.vlan_tagged_interface);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
- user = conf->eap_user;
- while (user) {
- prev_user = user;
- user = user->next;
- hostapd_config_free_eap_user(prev_user);
- }
+ hostapd_config_free_eap_users(conf->eap_user);
os_free(conf->eap_user_sqlite);
os_free(conf->eap_req_id_text);
@@ -477,7 +548,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
hostapd_config_free_vlan(conf);
os_free(conf->time_zone);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
{
struct ft_remote_r0kh *r0kh, *r0kh_prev;
struct ft_remote_r1kh *r1kh, *r1kh_prev;
@@ -498,7 +569,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(r1kh_prev);
}
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_WPS
os_free(conf->wps_pin_requests);
@@ -530,6 +601,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->roaming_consortium);
os_free(conf->venue_name);
+ os_free(conf->venue_url);
os_free(conf->nai_realm_data);
os_free(conf->network_auth_type);
os_free(conf->anqp_3gpp_cell_net);
@@ -559,17 +631,30 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(p->icons[j]);
os_free(p->icons);
os_free(p->osu_nai);
+ os_free(p->osu_nai2);
os_free(p->service_desc);
}
os_free(conf->hs20_osu_providers);
}
+ if (conf->hs20_operator_icon) {
+ size_t i;
+
+ for (i = 0; i < conf->hs20_operator_icon_count; i++)
+ os_free(conf->hs20_operator_icon[i]);
+ os_free(conf->hs20_operator_icon);
+ }
os_free(conf->subscr_remediation_url);
+ os_free(conf->t_c_filename);
+ os_free(conf->t_c_server_url);
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);
wpabuf_free(conf->assocresp_elements);
os_free(conf->sae_groups);
+#ifdef CONFIG_OWE
+ os_free(conf->owe_groups);
+#endif /* CONFIG_OWE */
os_free(conf->wowlan_triggers);
@@ -577,11 +662,22 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(conf->own_ie_override);
+ wpabuf_free(conf->sae_commit_override);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(conf->no_probe_resp_if_seen_on);
os_free(conf->no_auth_if_seen_on);
+ hostapd_config_free_fils_realms(conf);
+
+#ifdef CONFIG_DPP
+ os_free(conf->dpp_connector);
+ wpabuf_free(conf->dpp_netaccesskey);
+ wpabuf_free(conf->dpp_csign);
+#endif /* CONFIG_DPP */
+
+ hostapd_config_free_sae_passwords(conf);
+
os_free(conf);
}
@@ -802,7 +898,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
}
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
(bss->nas_identifier == NULL ||
os_strlen(bss->nas_identifier) < 1 ||
@@ -812,7 +908,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
"string");
return -1;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211N
if (full_config && conf->ieee80211n &&
@@ -848,6 +944,16 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
wpa_printf(MSG_ERROR,
"VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
}
+
+ if (full_config && conf->ieee80211ac && bss->wpa &&
+ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+ {
+ bss->disable_11ac = 1;
+ wpa_printf(MSG_ERROR,
+ "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
+ }
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_WPS
@@ -866,7 +972,9 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
if (full_config && bss->wps_state && bss->wpa &&
(!(bss->wpa & 2) ||
- !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)))) {
wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
"WPA2/CCMP/GCMP forced WPS to be disabled");
bss->wps_state = 0;
@@ -976,8 +1084,15 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss,
if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
bss->rsn_pairwise = bss->wpa_pairwise;
- bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
- bss->rsn_pairwise);
+ if (bss->group_cipher)
+ bss->wpa_group = bss->group_cipher;
+ else
+ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+ bss->wpa_pairwise,
+ bss->rsn_pairwise);
+ if (!bss->wpa_group_rekey_set)
+ bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
+ 600 : 86400;
if (full_config) {
bss->radius->auth_server = bss->radius->auth_servers;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8c8f7e286bda..778366d49afe 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -160,6 +160,8 @@ struct hostapd_eap_user {
} methods[EAP_MAX_METHODS];
u8 *password;
size_t password_len;
+ u8 *salt;
+ size_t salt_len; /* non-zero when password is salted */
int phase2;
int force_version;
unsigned int wildcard_prefix:1;
@@ -169,6 +171,7 @@ struct hostapd_eap_user {
unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
struct hostapd_radius_attr *accept_attr;
+ u32 t_c_timestamp;
};
struct hostapd_radius_attr {
@@ -201,6 +204,12 @@ struct hostapd_lang_string {
u8 name[252];
};
+struct hostapd_venue_url {
+ u8 venue_number;
+ u8 url_len;
+ u8 url[254];
+};
+
#define MAX_NAI_REALMS 10
#define MAX_NAI_REALMLEN 255
#define MAX_NAI_EAP_METHODS 5
@@ -224,6 +233,18 @@ struct anqp_element {
struct wpabuf *payload;
};
+struct fils_realm {
+ struct dl_list list;
+ u8 hash[2];
+ char realm[];
+};
+
+struct sae_password_entry {
+ struct sae_password_entry *next;
+ char *password;
+ char *identifier;
+ u8 peer_addr[ETH_ALEN];
+};
/**
* struct hostapd_bss_config - Per-BSS configuration
@@ -242,7 +263,8 @@ struct hostapd_bss_config {
int max_num_sta; /* maximum number of STAs in station table */
int dtim_period;
- int bss_load_update_period;
+ unsigned int bss_load_update_period;
+ unsigned int chan_util_avg_period;
int ieee802_1x; /* use IEEE 802.1X */
int eapol_version;
@@ -287,7 +309,7 @@ struct hostapd_bss_config {
char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
* frames */
- enum {
+ enum macaddr_acl {
ACCEPT_UNLESS_DENIED = 0,
DENY_UNLESS_ACCEPTED = 1,
USE_EXTERNAL_RADIUS_AUTH = 2
@@ -319,27 +341,37 @@ struct hostapd_bss_config {
PSK_RADIUS_REQUIRED = 2
} wpa_psk_radius;
int wpa_pairwise;
+ int group_cipher; /* wpa_group value override from configuation */
int wpa_group;
int wpa_group_rekey;
+ int wpa_group_rekey_set;
int wpa_strict_rekey;
int wpa_gmk_rekey;
int wpa_ptk_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
+ int wpa_disable_eapol_key_retries;
int rsn_pairwise;
int rsn_preauth;
char *rsn_preauth_interfaces;
- int peerkey;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
/* IEEE 802.11r - Fast BSS Transition */
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r1_key_holder[FT_R1KH_ID_LEN];
- u32 r0_key_lifetime;
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+ int rkh_pos_timeout;
+ int rkh_neg_timeout;
+ int rkh_pull_timeout; /* ms */
+ int rkh_pull_retries;
u32 reassociation_deadline;
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
int pmk_r1_push;
int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+ int ft_psk_generate_local;
+ int r1_max_key_lifetime;
+#endif /* CONFIG_IEEE80211R_AP */
char *ctrl_interface; /* directory for UNIX domain sockets */
#ifndef CONFIG_NATIVE_WINDOWS
@@ -353,6 +385,7 @@ struct hostapd_bss_config {
char *private_key_passwd;
int check_crl;
unsigned int tls_session_lifetime;
+ unsigned int tls_flags;
char *ocsp_stapling_response;
char *ocsp_stapling_response_multi;
char *dh_file;
@@ -464,6 +497,7 @@ struct hostapd_bss_config {
int time_advertisement;
char *time_zone;
int wnm_sleep_mode;
+ int wnm_sleep_mode_no_keys;
int bss_transition;
/* IEEE 802.11u - Interworking */
@@ -486,6 +520,10 @@ struct hostapd_bss_config {
unsigned int venue_name_count;
struct hostapd_lang_string *venue_name;
+ /* Venue URL duples */
+ unsigned int venue_url_count;
+ struct hostapd_venue_url *venue_url;
+
/* IEEE 802.11u - Network Authentication Type */
u8 *network_auth_type;
size_t network_auth_type_len;
@@ -508,7 +546,7 @@ struct hostapd_bss_config {
struct dl_list anqp_elem; /* list of struct anqp_element */
u16 gas_comeback_delay;
- int gas_frag_limit;
+ size_t gas_frag_limit;
int gas_address3;
u8 qos_map_set[16 + 2 * 21];
@@ -547,13 +585,20 @@ struct hostapd_bss_config {
char **icons;
size_t icons_count;
char *osu_nai;
+ char *osu_nai2;
unsigned int service_desc_count;
struct hostapd_lang_string *service_desc;
} *hs20_osu_providers, *last_osu;
size_t hs20_osu_providers_count;
+ size_t hs20_osu_providers_nai_count;
+ char **hs20_operator_icon;
+ size_t hs20_operator_icon_count;
unsigned int hs20_deauth_req_timeout;
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *t_c_filename;
+ u32 t_c_timestamp;
+ char *t_c_server_url;
#endif /* CONFIG_HS20 */
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -566,7 +611,10 @@ struct hostapd_bss_config {
struct wpabuf *assocresp_elements;
unsigned int sae_anti_clogging_threshold;
+ unsigned int sae_sync;
+ int sae_require_mfp;
int *sae_groups;
+ struct sae_password_entry *sae_passwords;
char *wowlan_triggers; /* Wake-on-WLAN triggers */
@@ -574,6 +622,8 @@ struct hostapd_bss_config {
u8 bss_load_test[5];
u8 bss_load_test_set;
struct wpabuf *own_ie_override;
+ int sae_reflection_attack;
+ struct wpabuf *sae_commit_override;
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
@@ -591,12 +641,71 @@ struct hostapd_bss_config {
#ifdef CONFIG_MBO
int mbo_enabled;
+ /**
+ * oce - Enable OCE in AP and/or STA-CFON mode
+ * - BIT(0) is Reserved
+ * - Set BIT(1) to enable OCE in STA-CFON mode
+ * - Set BIT(2) to enable OCE in AP mode
+ */
+ unsigned int oce;
+ int mbo_cell_data_conn_pref;
#endif /* CONFIG_MBO */
int ftm_responder;
int ftm_initiator;
+
+#ifdef CONFIG_FILS
+ u8 fils_cache_id[FILS_CACHE_ID_LEN];
+ int fils_cache_id_set;
+ struct dl_list fils_realms; /* list of struct fils_realm */
+ int fils_dh_group;
+ struct hostapd_ip_addr dhcp_server;
+ int dhcp_rapid_commit_proxy;
+ unsigned int fils_hlp_wait_time;
+ u16 dhcp_server_port;
+ u16 dhcp_relay_port;
+#endif /* CONFIG_FILS */
+
+ int multicast_to_unicast;
+
+ int broadcast_deauth;
+
+#ifdef CONFIG_DPP
+ char *dpp_connector;
+ struct wpabuf *dpp_netaccesskey;
+ unsigned int dpp_netaccesskey_expiry;
+ struct wpabuf *dpp_csign;
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_OWE
+ macaddr owe_transition_bssid;
+ u8 owe_transition_ssid[SSID_MAX_LEN];
+ size_t owe_transition_ssid_len;
+ char owe_transition_ifname[IFNAMSIZ + 1];
+ int *owe_groups;
+#endif /* CONFIG_OWE */
+
+ int coloc_intf_reporting;
};
+/**
+ * struct he_phy_capabilities_info - HE PHY capabilities
+ */
+struct he_phy_capabilities_info {
+ Boolean he_su_beamformer;
+ Boolean he_su_beamformee;
+ Boolean he_mu_beamformer;
+};
+
+/**
+ * struct he_operation - HE operation
+ */
+struct he_operation {
+ u8 he_bss_color;
+ u8 he_default_pe_duration;
+ u8 he_twt_required;
+ u8 he_rts_threshold;
+};
/**
* struct hostapd_config - Per-radio interface configuration
@@ -612,6 +721,7 @@ struct hostapd_config {
u8 channel;
u8 acs;
struct wpa_freq_range_list acs_ch_list;
+ int acs_exclude_dfs;
enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
enum {
LONG_PREAMBLE = 0,
@@ -620,6 +730,8 @@ struct hostapd_config {
int *supported_rates;
int *basic_rates;
+ unsigned int beacon_rate;
+ enum beacon_rate_type rate_type;
const struct wpa_driver_ops *driver;
char *driver_params;
@@ -635,6 +747,9 @@ struct hostapd_config {
* ' ' (ascii 32): all environments
* 'O': Outdoor environemnt only
* 'I': Indoor environment only
+ * 'X': Used with noncountry entity ("XXX")
+ * 0x00..0x31: identifying IEEE 802.11 standard
+ * Annex E table (0x04 = global table)
*/
int ieee80211d;
@@ -675,6 +790,7 @@ struct hostapd_config {
u8 vht_oper_chwidth;
u8 vht_oper_centr_freq_seg0_idx;
u8 vht_oper_centr_freq_seg1_idx;
+ u8 ht40_plus_minus_allowed;
/* Use driver-generated interface addresses when adding multiple BSSs */
u8 use_driver_iface_addr;
@@ -707,6 +823,18 @@ struct hostapd_config {
struct wpabuf *lci;
struct wpabuf *civic;
+ int stationary_ap;
+
+ int ieee80211ax;
+#ifdef CONFIG_IEEE80211AX
+ struct he_phy_capabilities_info he_phy_capab;
+ struct he_operation he_op;
+#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;
};
@@ -714,6 +842,7 @@ int hostapd_mac_comp(const void *a, const void *b);
struct hostapd_config * hostapd_config_defaults(void);
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
void hostapd_config_free_bss(struct hostapd_bss_config *conf);
void hostapd_config_free(struct hostapd_config *conf);
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f1394654d3a8..067cf863e84c 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -19,6 +19,7 @@
#include "ap_config.h"
#include "p2p_hostapd.h"
#include "hs20.h"
+#include "wpa_auth.h"
#include "ap_drv_ops.h"
@@ -99,6 +100,13 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
goto fail;
#endif /* CONFIG_FST */
+#ifdef CONFIG_FILS
+ pos = hostapd_eid_fils_indic(hapd, buf, 0);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* CONFIG_FILS */
+
if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
goto fail;
@@ -168,7 +176,8 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
- if (hapd->conf->mbo_enabled) {
+ if (hapd->conf->mbo_enabled ||
+ OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
add_buf_data(&proberesp, buf, pos - buf) < 0 ||
@@ -177,6 +186,13 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OWE
+ pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* CONFIG_OWE */
+
add_buf(&beacon, hapd->conf->vendor_elements);
add_buf(&proberesp, hapd->conf->vendor_elements);
add_buf(&assocresp, hapd->conf->assocresp_elements);
@@ -340,10 +356,44 @@ int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
u16 seq, u16 status, const u8 *ie, size_t len)
{
+ struct wpa_driver_sta_auth_params params;
+#ifdef CONFIG_FILS
+ struct sta_info *sta;
+#endif /* CONFIG_FILS */
+
if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
return 0;
- return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
- seq, status, ie, len);
+
+ os_memset(¶ms, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for sta_auth processing",
+ MAC2STR(addr));
+ return 0;
+ }
+
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ params.fils_auth = 1;
+ wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
+ params.fils_snonce,
+ params.fils_kek,
+ ¶ms.fils_kek_len);
+ }
+#endif /* CONFIG_FILS */
+
+ params.own_addr = hapd->own_addr;
+ params.addr = addr;
+ params.seq = seq;
+ params.status = status;
+ params.ie = ie;
+ params.len = len;
+
+ return hapd->driver->sta_auth(hapd->drv_priv, ¶ms);
}
@@ -554,13 +604,13 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
- u16 *flags)
+ u16 *flags, u8 *dfs_domain)
{
if (hapd->driver == NULL ||
hapd->driver->get_hw_feature_data == NULL)
return NULL;
return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
- flags);
+ flags, dfs_domain);
}
@@ -694,6 +744,15 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
sta = ap_get_sta(hapd, dst);
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
bssid = wildcard_bssid;
+ } else if (is_broadcast_ether_addr(dst) &&
+ len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+ /*
+ * The only current use case of Public Action frames with
+ * broadcast destination address is DPP PKEX. That case is
+ * directing all devices and not just the STAs within the BSS,
+ * so have to use the wildcard BSSID value.
+ */
+ bssid = wildcard_bssid;
}
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
hapd->own_addr, bssid, data, len, 0);
@@ -774,7 +833,9 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
if ((acs_ch_list_all ||
freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
chan->chan)) &&
- !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ !(hapd->iface->conf->acs_exclude_dfs &&
+ (chan->flag & HOSTAPD_CHAN_RADAR)))
int_array_add_unique(freq_list, chan->freq);
}
}
@@ -829,6 +890,9 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd)
&hapd->iface->conf->acs_ch_list,
chan->chan))
continue;
+ if (hapd->iface->conf->acs_exclude_dfs &&
+ (chan->flag & HOSTAPD_CHAN_RADAR))
+ continue;
if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
channels[num_channels++] = chan->chan;
int_array_add_unique(&freq_list, chan->freq);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 0bb7954ec061..db93fde7d6e3 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -72,7 +72,7 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time);
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
- u16 *flags);
+ u16 *flags, u8 *dfs_domain);
int hostapd_driver_commit(struct hostapd_data *hapd);
int hostapd_drv_none(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
@@ -103,6 +103,14 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
unsigned int freq,
unsigned int wait, const u8 *dst,
const u8 *data, size_t len);
+static inline void
+hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
+{
+ if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
+ !hapd->drv_priv)
+ return;
+ hapd->driver->send_action_cancel_wait(hapd->drv_priv);
+}
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
u16 auth_alg);
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
@@ -274,8 +282,9 @@ static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
- if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
- return -ENOTSUP;
+ if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
+ hapd->drv_priv == NULL)
+ return -1;
return hapd->driver->switch_channel(hapd->drv_priv, settings);
}
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index e7308a01d743..db8a26759c28 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -57,7 +57,11 @@ void mlme_authenticate_indication(struct hostapd_data *hapd,
HOSTAPD_LEVEL_DEBUG,
"MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
- if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK &&
+ !(sta->flags & WLAN_STA_MFP))
mlme_deletekeys_request(hapd, sta);
ap_sta_clear_disconnect_timeouts(hapd, sta);
}
@@ -105,7 +109,10 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
HOSTAPD_LEVEL_DEBUG,
"MLME-ASSOCIATE.indication(" MACSTR ")",
MAC2STR(sta->addr));
- if (sta->auth_alg != WLAN_AUTH_FT)
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK)
mlme_deletekeys_request(hapd, sta);
ap_sta_clear_disconnect_timeouts(hapd, sta);
}
@@ -130,7 +137,10 @@ void mlme_reassociate_indication(struct hostapd_data *hapd,
HOSTAPD_LEVEL_DEBUG,
"MLME-REASSOCIATE.indication(" MACSTR ")",
MAC2STR(sta->addr));
- if (sta->auth_alg != WLAN_AUTH_FT)
+ if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK)
mlme_deletekeys_request(hapd, sta);
ap_sta_clear_disconnect_timeouts(hapd, sta);
}
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index cdb49cdd9d32..95d004ed2b16 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -71,19 +71,26 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
}
if (eap_user->password) {
- user->password = os_malloc(eap_user->password_len);
+ user->password = os_memdup(eap_user->password,
+ eap_user->password_len);
if (user->password == NULL)
goto out;
- os_memcpy(user->password, eap_user->password,
- eap_user->password_len);
user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
}
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
user->remediation = eap_user->remediation;
user->accept_attr = eap_user->accept_attr;
+ user->t_c_timestamp = eap_user->t_c_timestamp;
rv = 0;
out:
@@ -129,10 +136,12 @@ 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.t_c_server_url = conf->t_c_server_url;
#endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp;
srv.erp_domain = conf->erp_domain;
srv.tls_session_lifetime = conf->tls_session_lifetime;
+ srv.tls_flags = conf->tls_flags;
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
@@ -146,6 +155,40 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
#endif /* RADIUS_SERVER */
+#ifdef EAP_TLS_FUNCS
+static void authsrv_tls_event(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ switch (ev) {
+ case TLS_CERT_CHAIN_SUCCESS:
+ wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success");
+ break;
+ case TLS_CERT_CHAIN_FAILURE:
+ wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'",
+ data->cert_fail.reason,
+ data->cert_fail.depth,
+ data->cert_fail.subject,
+ data->cert_fail.reason_txt);
+ break;
+ case TLS_PEER_CERTIFICATE:
+ wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s",
+ data->peer_cert.depth,
+ data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A",
+ data->peer_cert.subject);
+ break;
+ case TLS_ALERT:
+ if (data->alert.is_local)
+ wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s",
+ data->alert.description);
+ else
+ wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
+ data->alert.description);
+ break;
+ }
+}
+#endif /* EAP_TLS_FUNCS */
+
+
int authsrv_init(struct hostapd_data *hapd)
{
#ifdef EAP_TLS_FUNCS
@@ -157,6 +200,9 @@ int authsrv_init(struct hostapd_data *hapd)
os_memset(&conf, 0, sizeof(conf));
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ conf.tls_flags = hapd->conf->tls_flags;
+ conf.event_cb = authsrv_tls_event;
+ conf.cb_ctx = hapd;
hapd->ssl_ctx = tls_init(&conf);
if (hapd->ssl_ctx == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize TLS");
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 233320d2e978..59bd4af395d7 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -16,6 +16,7 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
@@ -30,6 +31,7 @@
#include "hs20.h"
#include "dfs.h"
#include "taxonomy.h"
+#include "ieee802_11_auth.h"
#ifdef NEED_AP_MLME
@@ -392,7 +394,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
2 + sizeof(struct ieee80211_vht_operation);
}
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax) {
+ buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
buflen += hostapd_mbo_ie_len(hapd);
+ buflen += hostapd_eid_owe_trans_len(hapd);
resp = os_zalloc(buflen);
if (resp == NULL)
@@ -443,8 +453,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
/* Extended supported rates */
pos = hostapd_eid_ext_supp_rates(hapd, pos);
- /* RSN, MDIE, WPA */
- pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+ /* RSN, MDIE */
+ if (hapd->conf->wpa != WPA_PROTO_WPA)
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
@@ -491,10 +502,26 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
pos = hostapd_eid_txpower_envelope(hapd, pos);
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
}
+#endif /* CONFIG_IEEE80211AC */
+
+ pos = hostapd_eid_fils_indic(hapd, pos, 0);
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax) {
+ pos = hostapd_eid_he_capab(hapd, pos);
+ pos = hostapd_eid_he_operation(hapd, pos);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
pos = hostapd_eid_vendor_vht(hapd, pos);
#endif /* CONFIG_IEEE80211AC */
+ /* WPA */
+ if (hapd->conf->wpa == WPA_PROTO_WPA)
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
@@ -526,6 +553,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
+ pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
@@ -618,7 +646,7 @@ static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
}
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
{
struct hostapd_sta_info *info;
@@ -628,6 +656,7 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
dl_list_del(&info->list);
dl_list_add_tail(&iface->sta_seen, &info->list);
os_get_reltime(&info->last_seen);
+ info->ssi_signal = ssi_signal;
return;
}
@@ -637,6 +666,7 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
return;
os_memcpy(info->addr, addr, ETH_ALEN);
os_get_reltime(&info->last_seen);
+ info->ssi_signal = ssi_signal;
if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
/* Expire oldest entry to make room for a new one */
@@ -707,14 +737,30 @@ void handle_probe_req(struct hostapd_data *hapd,
int ret;
u16 csa_offs[2];
size_t csa_offs_len;
+ u32 session_timeout, acct_interim_interval;
+ struct vlan_description vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
+ char *identity = NULL;
+ char *radius_cui = NULL;
if (len < IEEE80211_HDRLEN)
return;
ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
if (hapd->iconf->track_sta_max_num)
- sta_track_add(hapd->iface, mgmt->sa);
+ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
ie_len = len - IEEE80211_HDRLEN;
+ ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
+ &session_timeout,
+ &acct_interim_interval, &vlan_id,
+ &psk, &identity, &radius_cui, 1);
+ if (ret == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Probe Request frame from " MACSTR
+ " due to ACL reject ", MAC2STR(mgmt->sa));
+ return;
+ }
+
for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
mgmt->sa, mgmt->da, mgmt->bssid,
@@ -909,6 +955,9 @@ void handle_probe_req(struct hostapd_data *hapd,
}
#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
+ " signal=%d", MAC2STR(mgmt->sa), ssi_signal);
+
resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
&resp_len);
if (resp == NULL)
@@ -1033,7 +1082,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax) {
+ tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
tail_len += hostapd_mbo_ie_len(hapd);
+ tail_len += hostapd_eid_owe_trans_len(hapd);
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
@@ -1100,9 +1157,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
/* Extended supported rates */
tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
- /* RSN, MDIE, WPA */
- tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
- tailpos);
+ /* RSN, MDIE */
+ if (hapd->conf->wpa != WPA_PROTO_WPA)
+ tailpos = hostapd_eid_wpa(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE -
+ tailpos);
tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE -
@@ -1155,10 +1214,28 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
}
+#endif /* CONFIG_IEEE80211AC */
+
+ tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax) {
+ tailpos = hostapd_eid_he_capab(hapd, tailpos);
+ tailpos = hostapd_eid_he_operation(hapd, tailpos);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
#endif /* CONFIG_IEEE80211AC */
+ /* WPA */
+ if (hapd->conf->wpa == WPA_PROTO_WPA)
+ tailpos = hostapd_eid_wpa(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE -
+ tailpos);
+
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
@@ -1189,6 +1266,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
+ tailpos = hostapd_eid_owe_trans(hapd, tailpos,
+ tail + tail_len - tailpos);
if (hapd->conf->vendor_elements) {
os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
@@ -1211,6 +1290,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
params->dtim_period = hapd->conf->dtim_period;
params->beacon_int = hapd->iconf->beacon_int;
params->basic_rates = hapd->iface->basic_rates;
+ params->beacon_rate = hapd->iconf->beacon_rate;
+ params->rate_type = hapd->iconf->rate_type;
params->ssid = hapd->conf->ssid.ssid;
params->ssid_len = hapd->conf->ssid.ssid_len;
if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
@@ -1274,6 +1355,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
params->osen = 1;
}
#endif /* CONFIG_HS20 */
+ params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
params->pbss = hapd->conf->pbss;
return 0;
}
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index fc711815cf65..a26e30879cf5 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -21,7 +21,7 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface);
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params);
void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
void sta_track_del(struct hostapd_sta_info *info);
void sta_track_expire(struct hostapd_iface *iface, int force);
struct hostapd_data *
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
index fb639423230c..725d3cd3469b 100644
--- a/src/ap/bss_load.c
+++ b/src/ap/bss_load.c
@@ -16,11 +16,35 @@
#include "beacon.h"
+static int get_bss_load_update_timeout(struct hostapd_data *hapd,
+ unsigned int *sec, unsigned int *usec)
+{
+ unsigned int update_period = hapd->conf->bss_load_update_period;
+ unsigned int beacon_int = hapd->iconf->beacon_int;
+ unsigned int update_timeout;
+
+ if (!update_period || !beacon_int) {
+ wpa_printf(MSG_ERROR,
+ "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)",
+ update_period, beacon_int);
+ return -1;
+ }
+
+ update_timeout = update_period * beacon_int;
+
+ *sec = ((update_timeout / 1000) * 1024) / 1000;
+ *usec = (update_timeout % 1000) * 1024;
+
+ return 0;
+}
+
+
static void update_channel_utilization(void *eloop_data, void *user_data)
{
struct hostapd_data *hapd = eloop_data;
unsigned int sec, usec;
int err;
+ struct hostapd_iface *iface = hapd->iface;
if (!(hapd->beacon_set_done && hapd->started))
return;
@@ -33,8 +57,24 @@ static void update_channel_utilization(void *eloop_data, void *user_data)
ieee802_11_set_beacon(hapd);
- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
- usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
+ return;
+
+ if (hapd->conf->chan_util_avg_period) {
+ iface->chan_util_samples_sum += iface->channel_utilization;
+ iface->chan_util_num_sample_periods +=
+ hapd->conf->bss_load_update_period;
+ if (iface->chan_util_num_sample_periods >=
+ hapd->conf->chan_util_avg_period) {
+ iface->chan_util_average =
+ iface->chan_util_samples_sum /
+ (iface->chan_util_num_sample_periods /
+ hapd->conf->bss_load_update_period);
+ iface->chan_util_samples_sum = 0;
+ iface->chan_util_num_sample_periods = 0;
+ }
+ }
+
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
NULL);
}
@@ -42,17 +82,11 @@ static void update_channel_utilization(void *eloop_data, void *user_data)
int bss_load_update_init(struct hostapd_data *hapd)
{
- struct hostapd_bss_config *conf = hapd->conf;
- struct hostapd_config *iconf = hapd->iconf;
unsigned int sec, usec;
- if (!conf->bss_load_update_period || !iconf->beacon_int)
+ if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0)
return -1;
- hapd->bss_load_update_timeout = conf->bss_load_update_period *
- iconf->beacon_int;
- sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
- usec = (hapd->bss_load_update_timeout % 1000) * 1024;
eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
NULL);
return 0;
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 3680fda3153f..21b813ee18d7 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -26,23 +26,141 @@
#include "taxonomy.h"
+static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
+ size_t curr_len, const u8 *mcs_set)
+{
+ int ret;
+ size_t len = curr_len;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "ht_mcs_bitmask=");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* 77 first bits (+ 3 reserved bits) */
+ len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
+
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (os_snprintf_error(buflen - len, ret))
+ return curr_len;
+ len += ret;
+
+ return len;
+}
+
+
static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
struct sta_info *sta,
char *buf, size_t buflen)
{
struct hostap_sta_driver_data data;
int ret;
+ int len = 0;
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
return 0;
ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
- "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n",
+ "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
+ "signal=%d\n",
data.rx_packets, data.tx_packets,
- data.rx_bytes, data.tx_bytes, data.inactive_msec);
+ data.rx_bytes, data.tx_bytes, data.inactive_msec,
+ data.signal);
if (os_snprintf_error(buflen, ret))
return 0;
- return ret;
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
+ data.current_rx_rate);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ if (data.flags & STA_DRV_DATA_RX_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+ data.rx_mcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+ data.rx_vhtmcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+ data.rx_vht_nss);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
+ ret = os_snprintf(buf + len, buflen - len, " shortGI");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
+ data.current_tx_rate);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ if (data.flags & STA_DRV_DATA_TX_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " mcs %u",
+ data.tx_mcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
+ data.tx_vhtmcs);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
+ ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
+ data.tx_vht_nss);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
+ ret = os_snprintf(buf + len, buflen - len, " shortGI");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "rx_vht_mcs_map=%04x\n"
+ "tx_vht_mcs_map=%04x\n",
+ le_to_host16(sta->vht_capabilities->
+ vht_supported_mcs_set.rx_map),
+ le_to_host16(sta->vht_capabilities->
+ vht_supported_mcs_set.tx_map));
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ sta->ht_capabilities->
+ supported_mcs_set);
+ }
+
+ if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "last_ack_signal=%d\n", data.last_ack_rssi);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+ return len;
}
@@ -176,6 +294,53 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
len += os_snprintf(buf + len, buflen - len, "\n");
}
+ if (sta->power_capab) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "min_txpower=%d\n"
+ "max_txpower=%d\n",
+ sta->min_tx_power, sta->max_tx_power);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
+#ifdef CONFIG_IEEE80211AC
+ if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
+ res = os_snprintf(buf + len, buflen - len,
+ "vht_caps_info=0x%08x\n",
+ le_to_host32(sta->vht_capabilities->
+ vht_capabilities_info));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211AC */
+
+#ifdef CONFIG_IEEE80211N
+ if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
+ res = os_snprintf(buf + len, buflen - len,
+ "ht_caps_info=0x%04x\n",
+ le_to_host16(sta->ht_capabilities->
+ ht_capabilities_info));
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_IEEE80211N */
+
+ if (sta->ext_capability &&
+ buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
+ len += os_snprintf(buf + len, buflen - len, "ext_capab=");
+ len += wpa_snprintf_hex(buf + len, buflen - len,
+ sta->ext_capability + 1,
+ sta->ext_capability[0]);
+ len += os_snprintf(buf + len, buflen - len, "\n");
+ }
+
+ if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "wds_sta_ifname=%s\n", sta->ifname_wds);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
@@ -477,7 +642,8 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
size_t buflen)
{
struct hostapd_iface *iface = hapd->iface;
- int len = 0, ret;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ int len = 0, ret, j;
size_t i;
ret = os_snprintf(buf + len, buflen - len,
@@ -537,13 +703,17 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
"channel=%u\n"
"secondary_channel=%d\n"
"ieee80211n=%d\n"
- "ieee80211ac=%d\n",
+ "ieee80211ac=%d\n"
+ "beacon_int=%u\n"
+ "dtim_period=%d\n",
iface->conf->channel,
iface->conf->ieee80211n && !hapd->conf->disable_11n ?
iface->conf->secondary_channel : 0,
iface->conf->ieee80211n && !hapd->conf->disable_11n,
iface->conf->ieee80211ac &&
- !hapd->conf->disable_11ac);
+ !hapd->conf->disable_11ac,
+ iface->conf->beacon_int,
+ hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -551,15 +721,76 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
ret = os_snprintf(buf + len, buflen - len,
"vht_oper_chwidth=%d\n"
"vht_oper_centr_freq_seg0_idx=%d\n"
- "vht_oper_centr_freq_seg1_idx=%d\n",
+ "vht_oper_centr_freq_seg1_idx=%d\n"
+ "vht_caps_info=%08x\n",
iface->conf->vht_oper_chwidth,
iface->conf->vht_oper_centr_freq_seg0_idx,
- iface->conf->vht_oper_centr_freq_seg1_idx);
+ iface->conf->vht_oper_centr_freq_seg1_idx,
+ iface->conf->vht_capab);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
+ if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
+ u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
+ u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "rx_vht_mcs_map=%04x\n"
+ "tx_vht_mcs_map=%04x\n",
+ rxmap, txmap);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "ht_caps_info=%04x\n",
+ hapd->iconf->ht_capab);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
+ len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
+ mode->mcs_set);
+ }
+
+ if (iface->current_rates && iface->num_rates) {
+ ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ for (j = 0; j < iface->num_rates; j++) {
+ ret = os_snprintf(buf + len, buflen - len, "%s%02x",
+ j > 0 ? " " : "",
+ iface->current_rates[j].rate / 5);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ ret = os_snprintf(buf + len, buflen - len, "\n");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ for (j = 0; mode && j < mode->num_channels; j++) {
+ if (mode->channels[j].freq == iface->freq) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "max_txpower=%u\n",
+ mode->channels[j].max_tx_power);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ break;
+ }
+ }
+
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss = iface->bss[i];
ret = os_snprintf(buf + len, buflen - len,
@@ -578,6 +809,15 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
len += ret;
}
+ if (hapd->conf->chan_util_avg_period) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "chan_util_avg=%u\n",
+ iface->chan_util_average);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
return len;
}
@@ -639,3 +879,108 @@ void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
{
wpa_auth_pmksa_flush(hapd->wpa_auth);
}
+
+
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
+{
+ u8 spa[ETH_ALEN];
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ char *pos, *pos2;
+ int akmp = 0, expiration = 0;
+
+ /*
+ * Entry format:
+ *
+ */
+
+ if (hwaddr_aton(cmd, spa))
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+
+ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+ return -1;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (!pos2)
+ return -1;
+ pmk_len = (pos2 - pos) / 2;
+ if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
+ hexstr2bin(pos, pmk, pmk_len) < 0)
+ return -1;
+
+ pos = pos2 + 1;
+
+ if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
+ return -1;
+
+ return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
+ pmkid, expiration, akmp);
+}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+ const u8 *addr, char *buf, size_t len)
+{
+ return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
+}
+
+
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
+{
+ u8 spa[ETH_ALEN];
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN_MAX];
+ char *pos;
+ int expiration;
+
+ /*
+ * Entry format:
+ *
+ */
+
+ if (hwaddr_aton(cmd, spa))
+ return NULL;
+
+ pos = os_strchr(cmd, ' ');
+ if (!pos)
+ return NULL;
+ pos++;
+
+ if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+ return NULL;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return NULL;
+ pos++;
+
+ if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
+ return NULL;
+
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return NULL;
+ pos++;
+
+ if (sscanf(pos, "%d", &expiration) != 1)
+ return NULL;
+
+ return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 4f996800f132..d1dcebfb957d 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -32,5 +32,9 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
size_t len);
void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd);
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+ const u8 *addr, char *buf, size_t len);
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
#endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 47adba7ef726..993dd19c2cb0 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1,7 +1,7 @@
/*
* DFS - Dynamic Frequency Selection
* Copyright (c) 2002-2013, Jouni Malinen
- * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -747,6 +747,23 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
}
+static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface)
+{
+ int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+
+ /* Get the start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+ if (start_chan_idx < 0)
+ return 0;
+
+ /* Get the number of used channels, depending on width */
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Check if all channels are DFS available */
+ return dfs_check_chans_available(iface, start_chan_idx, n_chans);
+}
+
+
int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
@@ -767,8 +784,21 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
set_dfs_state(iface, freq, ht_enabled, chan_offset,
chan_width, cf1, cf2,
HOSTAPD_CHAN_DFS_AVAILABLE);
- iface->cac_started = 0;
- hostapd_setup_interface_complete(iface, 0);
+ /*
+ * Just mark the channel available when CAC completion
+ * event is received in enabled state. CAC result could
+ * have been propagated from another radio having the
+ * same regulatory configuration. When CAC completion is
+ * received during non-HAPD_IFACE_ENABLED state, make
+ * sure the configured channel is available because this
+ * CAC completion event could have been propagated from
+ * another radio.
+ */
+ if (iface->state != HAPD_IFACE_ENABLED &&
+ hostapd_config_dfs_chan_available(iface)) {
+ hostapd_setup_interface_complete(iface, 0);
+ iface->cac_started = 0;
+ }
}
}
@@ -776,6 +806,25 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
}
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+
+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+ return 0;
+}
+
+
static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
{
struct hostapd_channel_data *channel;
@@ -840,6 +889,13 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
if (iface->cac_started)
return hostapd_dfs_start_channel_switch_cac(iface);
+ /*
+ * Allow selection of DFS channel in ETSI to comply with
+ * uniform spreading.
+ */
+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ skip_radar = 0;
+
/* Perform channel switch/CSA */
channel = dfs_get_valid_channel(iface, &secondary_channel,
&vht_oper_centr_freq_seg0_idx,
@@ -1055,7 +1111,8 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
return 1;
}
- if (ieee80211_is_dfs(iface->freq)) {
+ if (ieee80211_is_dfs(iface->freq, iface->hw_features,
+ iface->num_hw_features)) {
wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
__func__, iface->freq);
return 0;
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
index be8c0e6001c9..f0fa6f688037 100644
--- a/src/ap/dfs.h
+++ b/src/ap/dfs.h
@@ -1,7 +1,7 @@
/*
* DFS - Dynamic Frequency Selection
* Copyright (c) 2002-2013, Jouni Malinen
- * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,6 +14,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface);
int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2);
+int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
int ht_enabled,
int chan_offset, int chan_width,
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index f0212fb2a984..6d8c2f4be0da 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -7,10 +7,9 @@
*/
#include "utils/includes.h"
-#include
-#include
#include "utils/common.h"
+#include "common/dhcp.h"
#include "l2_packet/l2_packet.h"
#include "hostapd.h"
#include "sta_info.h"
@@ -18,29 +17,6 @@
#include "x_snoop.h"
#include "dhcp_snoop.h"
-struct bootp_pkt {
- struct iphdr iph;
- struct udphdr udph;
- u8 op;
- u8 htype;
- u8 hlen;
- u8 hops;
- be32 xid;
- be16 secs;
- be16 flags;
- be32 client_ip;
- be32 your_ip;
- be32 server_ip;
- be32 relay_ip;
- u8 hw_addr[16];
- u8 serv_name[64];
- u8 boot_file[128];
- u8 exten[312];
-} STRUCT_PACKED;
-
-#define DHCPACK 5
-static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
-
static const char * ipaddr_str(u32 addr)
{
@@ -74,24 +50,26 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
if (tot_len > (unsigned int) (len - ETH_HLEN))
return;
- if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
+ if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
return;
/* Parse DHCP options */
end = (const u8 *) b + tot_len;
pos = &b->exten[4];
- while (pos < end && *pos != 0xff) {
+ while (pos < end && *pos != DHCP_OPT_END) {
const u8 *opt = pos++;
- if (*opt == 0) /* padding */
+ if (*opt == DHCP_OPT_PAD)
continue;
+ if (pos >= end || 1 + *pos > end - pos)
+ break;
pos += *pos + 1;
if (pos >= end)
break;
switch (*opt) {
- case 1: /* subnet mask */
+ case DHCP_OPT_SUBNET_MASK:
if (opt[1] == 4)
subnet_mask = WPA_GET_BE32(&opt[2]);
if (subnet_mask == 0)
@@ -101,7 +79,7 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
prefixlen--;
}
break;
- case 53: /* message type */
+ case DHCP_OPT_MSG_TYPE:
if (opt[1])
msgtype = opt[2];
break;
@@ -176,4 +154,5 @@ int dhcp_snoop_init(struct hostapd_data *hapd)
void dhcp_snoop_deinit(struct hostapd_data *hapd)
{
l2_packet_deinit(hapd->sock_dhcp);
+ hapd->sock_dhcp = NULL;
}
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
new file mode 100644
index 000000000000..149f389f789f
--- /dev/null
+++ b/src/ap/dpp_hostapd.c
@@ -0,0 +1,2096 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dpp.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+#include "wpa_auth.h"
+#include "dpp_hostapd.h"
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
+static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
+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
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+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);
+ 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,
+ "DPP: Sending out pending authentication response");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+ auth->peer_mac_addr,
+ wpabuf_head(hapd->dpp_auth->resp_msg),
+ wpabuf_len(hapd->dpp_auth->resp_msg));
+ }
+
+ return bi->id;
+}
+
+
+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)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Retry Authentication Response after timeout");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(auth->peer_mac_addr), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
+ wpabuf_head(auth->resp_msg),
+ wpabuf_len(auth->resp_msg));
+}
+
+
+static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ unsigned int wait_time, max_tries;
+
+ if (!auth || !auth->resp_msg)
+ return;
+
+ if (hapd->dpp_resp_max_tries)
+ max_tries = hapd->dpp_resp_max_tries;
+ else
+ max_tries = 5;
+ auth->auth_resp_tries++;
+ if (auth->auth_resp_tries >= max_tries) {
+ wpa_printf(MSG_INFO,
+ "DPP: No confirm received from initiator - stopping exchange");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (hapd->dpp_resp_retry_time)
+ wait_time = hapd->dpp_resp_retry_time;
+ else
+ wait_time = 1000;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Schedule retransmission of Authentication Response frame in %u ms",
+ wait_time);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+}
+
+
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t data_len, int ok)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
+ MAC2STR(dst), ok);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
+ " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
+
+ if (!hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore TX status since there is no ongoing authentication exchange");
+ return;
+ }
+
+ if (hapd->dpp_auth->remove_on_tx_status) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate authentication exchange due to an earlier error");
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+ NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (hapd->dpp_auth_ok_on_ack)
+ hostapd_dpp_auth_success(hapd, 1);
+
+ if (!is_broadcast_ether_addr(dst) && !ok) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unicast DPP Action frame was not ACKed");
+ if (auth->waiting_auth_resp) {
+ /* In case of DPP Authentication Request frame, move to
+ * the next channel immediately. */
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_auth_init_next(hapd);
+ return;
+ }
+ if (auth->waiting_auth_conf) {
+ hostapd_dpp_auth_resp_retry(hapd);
+ return;
+ }
+ }
+
+ if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
+ /* Allow timeout handling to stop iteration if no response is
+ * received from a peer that has ACKed a request. */
+ auth->auth_req_ack = 1;
+ }
+
+ if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
+ hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
+ hapd->dpp_auth->curr_freq,
+ hapd->dpp_auth->neg_freq);
+ hostapd_drv_send_action_cancel_wait(hapd);
+
+ if (hapd->dpp_auth->neg_freq !=
+ (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ hapd->dpp_auth->neg_freq, hapd->iface->freq);
+ }
+ }
+
+ if (hapd->dpp_auth_ok_on_ack)
+ hapd->dpp_auth_ok_on_ack = 0;
+}
+
+
+static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ unsigned int freq;
+ struct os_reltime now, diff;
+ unsigned int wait_time, diff_ms;
+
+ if (!auth || !auth->waiting_auth_resp)
+ return;
+
+ wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
+ diff_ms = diff.sec * 1000 + diff.usec / 1000;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
+ wait_time, diff_ms);
+
+ if (auth->auth_req_ack && diff_ms >= wait_time) {
+ /* Peer ACK'ed Authentication Request frame, but did not reply
+ * with Authentication Response frame within two seconds. */
+ wpa_printf(MSG_INFO,
+ "DPP: No response received from responder - stopping initiation attempt");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+
+ if (diff_ms >= wait_time) {
+ /* Authentication Request frame was not ACK'ed and no reply
+ * was receiving within two seconds. */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue Initiator channel iteration");
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ hostapd_dpp_auth_init_next(hapd);
+ return;
+ }
+
+ /* Driver did not support 2000 ms long wait_time with TX command, so
+ * schedule listen operation to continue waiting for the response.
+ *
+ * DPP listen operations continue until stopped, so simply schedule a
+ * new call to this function at the point when the two second reply
+ * wait has expired. */
+ wait_time -= diff_ms;
+
+ freq = auth->curr_freq;
+ if (auth->neg_freq > 0)
+ freq = auth->neg_freq;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue reply wait on channel %u MHz for %u ms",
+ freq, wait_time);
+ hapd->dpp_in_response_listen = 1;
+
+ if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ freq, hapd->iface->freq);
+ }
+
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reply_wait_timeout, hapd, NULL);
+}
+
+
+static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
+ struct dpp_authentication *auth)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->dpp_config_obj_override)
+ auth->config_obj_override =
+ os_strdup(hapd->dpp_config_obj_override);
+ if (hapd->dpp_discovery_override)
+ auth->discovery_override =
+ os_strdup(hapd->dpp_discovery_override);
+ if (hapd->dpp_groups_override)
+ auth->groups_override = os_strdup(hapd->dpp_groups_override);
+ auth->ignore_netaccesskey_mismatch =
+ hapd->dpp_ignore_netaccesskey_mismatch;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+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;
+
+ if (!hapd->dpp_auth)
+ return;
+ wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
+ hostapd_dpp_auth_init_next(hapd);
+}
+
+
+static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ const u8 *dst;
+ unsigned int wait_time, max_wait_time, freq, max_tries, used;
+ struct os_reltime now, diff;
+
+ if (!auth)
+ return -1;
+
+ if (auth->freq_idx == 0)
+ os_get_reltime(&hapd->dpp_init_iter_start);
+
+ if (auth->freq_idx >= auth->num_freq) {
+ auth->num_freq_iters++;
+ if (hapd->dpp_init_max_tries)
+ max_tries = hapd->dpp_init_max_tries;
+ else
+ max_tries = 5;
+ if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
+ wpa_printf(MSG_INFO,
+ "DPP: No response received from responder - stopping initiation attempt");
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_INIT_FAILED);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return -1;
+ }
+ auth->freq_idx = 0;
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ if (hapd->dpp_init_retry_time)
+ wait_time = hapd->dpp_init_retry_time;
+ else
+ wait_time = 10000;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
+ used = diff.sec * 1000 + diff.usec / 1000;
+ if (used > wait_time)
+ wait_time = 0;
+ else
+ wait_time -= used;
+ wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
+ wait_time);
+ eloop_register_timeout(wait_time / 1000,
+ (wait_time % 1000) * 1000,
+ hostapd_dpp_init_timeout, hapd,
+ NULL);
+ return 0;
+ }
+ freq = auth->freq[auth->freq_idx++];
+ auth->curr_freq = freq;
+
+ if (is_zero_ether_addr(auth->peer_bi->mac_addr))
+ dst = broadcast;
+ else
+ dst = auth->peer_bi->mac_addr;
+ hapd->dpp_auth_ok_on_ack = 0;
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+ max_wait_time = hapd->dpp_resp_wait_time ?
+ hapd->dpp_resp_wait_time : 2000;
+ if (wait_time > max_wait_time)
+ wait_time = max_wait_time;
+ wait_time += 10; /* give the driver some extra time to complete */
+ eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
+ hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ wait_time -= 10;
+ if (auth->neg_freq > 0 && freq != auth->neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
+ freq, auth->neg_freq);
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
+ auth->auth_req_ack = 0;
+ os_get_reltime(&hapd->dpp_last_init);
+ return hostapd_drv_send_action(hapd, freq, wait_time,
+ dst,
+ wpabuf_head(hapd->dpp_auth->req_msg),
+ wpabuf_len(hapd->dpp_auth->req_msg));
+}
+
+
+int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
+{
+ const char *pos;
+ struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
+ u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ unsigned int neg_freq = 0;
+
+ pos = os_strstr(cmd, " peer=");
+ if (!pos)
+ return -1;
+ pos += 6;
+ peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+ if (!peer_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find bootstrapping info for the identified peer");
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " own=");
+ if (pos) {
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+ if (!own_bi) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find bootstrapping info for the identified local entry");
+ return -1;
+ }
+
+ if (peer_bi->curve != own_bi->curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
+ peer_bi->curve->name, own_bi->curve->name);
+ return -1;
+ }
+ }
+
+ pos = os_strstr(cmd, " role=");
+ if (pos) {
+ pos += 6;
+ if (os_strncmp(pos, "configurator", 12) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strncmp(pos, "enrollee", 8) == 0)
+ allowed_roles = DPP_CAPAB_ENROLLEE;
+ else if (os_strncmp(pos, "either", 6) == 0)
+ allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ else
+ goto fail;
+ }
+
+ pos = os_strstr(cmd, " neg_freq=");
+ if (pos)
+ neg_freq = atoi(pos + 10);
+
+ if (hapd->dpp_auth) {
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
+ hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
+ NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+ dpp_auth_deinit(hapd->dpp_auth);
+ }
+
+ hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi,
+ allowed_roles, neg_freq,
+ hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
+ 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) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ goto fail;
+ }
+
+ hapd->dpp_auth->neg_freq = neg_freq;
+
+ if (!is_zero_ether_addr(peer_bi->mac_addr))
+ os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
+ ETH_ALEN);
+
+ return hostapd_dpp_auth_init_next(hapd);
+fail:
+ return -1;
+}
+
+
+int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
+{
+ int freq;
+
+ freq = atoi(cmd);
+ if (freq <= 0)
+ return -1;
+
+ if (os_strstr(cmd, " role=configurator"))
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ else if (os_strstr(cmd, " role=enrollee"))
+ hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
+ else
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
+ DPP_CAPAB_ENROLLEE;
+ hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
+
+ if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
+ /* TODO: Listen operation on non-operating channel */
+ wpa_printf(MSG_INFO,
+ "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
+ freq, hapd->iface->freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
+{
+ /* TODO: Stop listen operation on non-operating channel */
+}
+
+
+static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *r_bootstrap, *i_bootstrap;
+ u16 r_bootstrap_len, i_bootstrap_len;
+ struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
+ MAC2STR(src));
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+
+ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+
+ /* 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;
+ }
+
+ if (!own_bi) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No matching own bootstrapping key found - ignore message");
+ return;
+ }
+
+ if (hapd->dpp_auth) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Already in DPP authentication exchange - ignore new one");
+ return;
+ }
+
+ hapd->dpp_auth_ok_on_ack = 0;
+ hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles,
+ hapd->dpp_qr_mutual,
+ peer_bi, own_bi, freq, hdr, buf, len);
+ if (!hapd->dpp_auth) {
+ wpa_printf(MSG_DEBUG, "DPP: No response generated");
+ return;
+ }
+ hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
+ if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
+ hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+ os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d",
+ MAC2STR(src), hapd->dpp_auth->curr_freq,
+ DPP_PA_AUTHENTICATION_RESP);
+ hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
+ src, wpabuf_head(hapd->dpp_auth->resp_msg),
+ wpabuf_len(hapd->dpp_auth->resp_msg));
+}
+
+
+static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
+ struct dpp_authentication *auth)
+{
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
+ dpp_akm_str(auth->akm));
+ if (auth->ssid_len)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
+ wpa_ssid_txt(auth->ssid, auth->ssid_len));
+ if (auth->connector) {
+ /* TODO: Save the Connector and consider using a command
+ * to fetch the value instead of sending an event with
+ * it. The Connector could end up being larger than what
+ * most clients are ready to receive as an event
+ * message. */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
+ auth->connector);
+ } else if (auth->passphrase[0]) {
+ char hex[64 * 2 + 1];
+
+ wpa_snprintf_hex(hex, sizeof(hex),
+ (const u8 *) auth->passphrase,
+ os_strlen(auth->passphrase));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
+ hex);
+ } else if (auth->psk_set) {
+ char hex[PMK_LEN * 2 + 1];
+
+ wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
+ hex);
+ }
+ if (auth->c_sign_key) {
+ char *hex;
+ size_t hexlen;
+
+ hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1;
+ hex = os_malloc(hexlen);
+ if (hex) {
+ wpa_snprintf_hex(hex, hexlen,
+ wpabuf_head(auth->c_sign_key),
+ wpabuf_len(auth->c_sign_key));
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_C_SIGN_KEY "%s", hex);
+ os_free(hex);
+ }
+ }
+ if (auth->net_access_key) {
+ char *hex;
+ size_t hexlen;
+
+ hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
+ hex = os_malloc(hexlen);
+ if (hex) {
+ wpa_snprintf_hex(hex, hexlen,
+ wpabuf_head(auth->net_access_key),
+ wpabuf_len(auth->net_access_key));
+ if (auth->net_access_key_expiry)
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
+ (unsigned long)
+ auth->net_access_key_expiry);
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_NET_ACCESS_KEY "%s", hex);
+ os_free(hex);
+ }
+ }
+}
+
+
+static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct hostapd_data *hapd = ctx;
+ const u8 *pos;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->auth_success) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return;
+ }
+ if (!resp || status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
+ goto fail;
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
+ adv_proto);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
+ resp);
+
+ if (wpabuf_len(adv_proto) != 10 ||
+ !(pos = wpabuf_head(adv_proto)) ||
+ pos[0] != WLAN_EID_ADV_PROTO ||
+ pos[1] != 8 ||
+ pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[4] != 5 ||
+ WPA_GET_BE24(&pos[5]) != OUI_WFA ||
+ pos[8] != 0x1a ||
+ pos[9] != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Not a DPP Advertisement Protocol ID");
+ goto fail;
+ }
+
+ if (dpp_conf_resp_rx(auth, resp) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+ goto fail;
+ }
+
+ hostapd_dpp_handle_config_obj(hapd, auth);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+
+fail:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *buf, *conf_req;
+ char json[100];
+ int res;
+ int netrole_ap = 1;
+
+ os_snprintf(json, sizeof(json),
+ "{\"name\":\"Test\","
+ "\"wi-fi_tech\":\"infra\","
+ "\"netRole\":\"%s\"}",
+ 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) {
+ 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);
+
+ res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
+ buf, hostapd_dpp_gas_resp_cb, hapd);
+ if (res < 0) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Failed to send Query Request");
+ wpabuf_free(buf);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: GAS query started with dialog token %u", res);
+ }
+}
+
+
+static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
+{
+ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
+ initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ if (hapd->dpp_auth->configurator) {
+ /* Prevent GAS response */
+ hapd->dpp_auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!hapd->dpp_auth->configurator)
+ hostapd_dpp_start_gas_client(hapd);
+}
+
+
+static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
+ MAC2STR(src));
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - drop");
+ return;
+ }
+
+ if (!is_zero_ether_addr(auth->peer_mac_addr) &&
+ 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;
+ }
+
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+
+ if (auth->curr_freq != freq && auth->neg_freq == freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder accepted request for different negotiation channel");
+ auth->curr_freq = freq;
+ }
+
+ eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
+ msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+ if (!msg) {
+ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+ return;
+ }
+ os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), auth->curr_freq,
+ DPP_PA_AUTHENTICATION_CONF);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+ hapd->dpp_auth_ok_on_ack = 1;
+}
+
+
+static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
+ MAC2STR(src));
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - 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;
+ }
+
+ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+ return;
+ }
+
+ hostapd_dpp_auth_success(hapd, 0);
+}
+
+
+static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
+ const u8 *src, unsigned int freq,
+ u8 trans_id,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+
+ msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP,
+ 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector));
+ if (!msg)
+ return;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
+ goto skip_trans_id;
+ }
+ if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
+ trans_id ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, trans_id);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_trans_id:
+ if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
+ goto skip_connector;
+ }
+ if (status == DPP_STATUS_OK &&
+ dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
+ char *connector;
+
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
+ connector = dpp_corrupt_connector_signature(
+ hapd->conf->dpp_connector);
+ if (!connector) {
+ wpabuf_free(msg);
+ return;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(connector));
+ wpabuf_put_str(msg, connector);
+ os_free(connector);
+ goto skip_connector;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Connector */
+ if (status == DPP_STATUS_OK) {
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+ wpabuf_put_str(msg, hapd->conf->dpp_connector);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_connector:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
+ " status=%d", MAC2STR(src), status);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d status=%d", MAC2STR(src), freq,
+ DPP_PA_PEER_DISCOVERY_RESP, status);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
+static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
+ const u8 *src,
+ const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *connector, *trans_id;
+ u16 connector_len, trans_id_len;
+ struct os_time now;
+ struct dpp_introduction intro;
+ os_time_t expire;
+ int expiration;
+ enum dpp_status_error res;
+
+ wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
+ MAC2STR(src));
+ if (!hapd->wpa_auth ||
+ !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+ !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+ wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+ return;
+ }
+
+ if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+ !hapd->conf->dpp_csign) {
+ wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+ return;
+ }
+
+ os_get_time(&now);
+
+ if (hapd->conf->dpp_netaccesskey_expiry &&
+ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+ wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+ return;
+ }
+
+ trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ return;
+ }
+
+ connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
+ if (!connector) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include its Connector");
+ return;
+ }
+
+ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey),
+ wpabuf_head(hapd->conf->dpp_csign),
+ wpabuf_len(hapd->conf->dpp_csign),
+ connector, connector_len, &expire);
+ if (res == 255) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in internal failure (peer "
+ MACSTR ")", MAC2STR(src));
+ return;
+ }
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in failure (peer "
+ MACSTR " status %d)", MAC2STR(src), res);
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+ res);
+ return;
+ }
+
+ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+ expire = hapd->conf->dpp_netaccesskey_expiry;
+ if (expire)
+ expiration = expire - now.sec;
+ else
+ expiration = 0;
+
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ intro.pmkid, expiration,
+ WPA_KEY_MGMT_DPP) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+ return;
+ }
+
+ hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
+ DPP_STATUS_OK);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
+ MAC2STR(src));
+
+ /* TODO: Support multiple PKEX codes by iterating over all the enabled
+ * values here */
+
+ if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code configured - ignore request");
+ return;
+ }
+
+ if (hapd->dpp_pkex) {
+ /* TODO: Support parallel operations */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Already in PKEX session - ignore new request");
+ return;
+ }
+
+ hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
+ hapd->dpp_pkex_bi,
+ hapd->own_addr, src,
+ hapd->dpp_pkex_identifier,
+ hapd->dpp_pkex_code,
+ buf, len);
+ if (!hapd->dpp_pkex) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to process the request - ignore it");
+ return;
+ }
+
+ msg = hapd->dpp_pkex->exchange_resp;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_EXCHANGE_RESP);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ if (hapd->dpp_pkex->failed) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Terminate PKEX exchange due to an earlier error");
+ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+ hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ }
+}
+
+
+static void
+hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
+ MAC2STR(src));
+
+ /* TODO: Support multiple PKEX codes by iterating over all the enabled
+ * values here */
+
+ if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
+ hapd->dpp_pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
+ if (!msg) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
+ MAC2STR(src));
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_COMMIT_REVEAL_REQ);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct wpabuf *msg;
+ struct dpp_pkex *pkex = hapd->dpp_pkex;
+ struct dpp_bootstrap_info *bi;
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
+ MAC2STR(src));
+
+ if (!pkex || pkex->initiator || !pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
+ if (!msg) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
+ if (hapd->dpp_pkex->failed) {
+ wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
+ if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
+ hapd->dpp_pkex->own_bi->pkex_t =
+ hapd->dpp_pkex->t;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
+ MACSTR, MAC2STR(src));
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_COMMIT_REVEAL_RESP);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+
+ bi = os_zalloc(sizeof(*bi));
+ 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);
+}
+
+
+static void
+hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ int res;
+ struct dpp_bootstrap_info *bi, *own_bi;
+ struct dpp_pkex *pkex = hapd->dpp_pkex;
+ char cmd[500];
+
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
+ MAC2STR(src));
+
+ if (!pkex || !pkex->initiator || !pkex->exchange_done) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
+ return;
+ }
+
+ res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
+ return;
+ }
+
+ own_bi = pkex->own_bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ 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,
+ hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start authentication after PKEX with parameters: %s",
+ cmd);
+ if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication initialization failed");
+ return;
+ }
+}
+
+
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq)
+{
+ u8 crypto_suite;
+ enum dpp_public_action_frame_type type;
+ const u8 *hdr;
+ unsigned int pkex_t;
+
+ if (len < DPP_HDR_LEN)
+ return;
+ if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
+ return;
+ hdr = buf;
+ buf += 4;
+ len -= 4;
+ crypto_suite = *buf++;
+ type = *buf++;
+ len -= 2;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Public Action frame crypto suite %u type %d from "
+ MACSTR " freq=%u",
+ crypto_suite, type, MAC2STR(src), freq);
+ if (crypto_suite != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
+ crypto_suite);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=unsupported-crypto-suite",
+ MAC2STR(src), freq, type);
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
+ if (dpp_check_attrs(buf, len) < 0) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d ignore=invalid-attributes",
+ MAC2STR(src), freq, type);
+ return;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq, type);
+
+ switch (type) {
+ case DPP_PA_AUTHENTICATION_REQ:
+ hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_AUTHENTICATION_RESP:
+ hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
+ break;
+ case DPP_PA_AUTHENTICATION_CONF:
+ hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
+ break;
+ case DPP_PA_PEER_DISCOVERY_REQ:
+ hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
+ break;
+ case DPP_PA_PKEX_EXCHANGE_REQ:
+ hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq);
+ break;
+ case DPP_PA_PKEX_EXCHANGE_RESP:
+ hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
+ break;
+ case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
+ hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
+ hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
+ freq);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignored unsupported frame subtype %d", type);
+ break;
+ }
+
+ if (hapd->dpp_pkex)
+ pkex_t = hapd->dpp_pkex->t;
+ else if (hapd->dpp_pkex_bi)
+ pkex_t = hapd->dpp_pkex_bi->pkex_t;
+ else
+ pkex_t = 0;
+ if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
+ hostapd_dpp_pkex_remove(hapd, "*");
+ }
+}
+
+
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+ const u8 *query, size_t query_len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ struct wpabuf *resp;
+
+ wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
+ if (!auth || !auth->auth_success ||
+ os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Received Configuration Request (GAS Query Request)",
+ query, query_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
+ MAC2STR(sa));
+ resp = dpp_conf_req_rx(auth, query, query_len);
+ if (!resp)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ return resp;
+}
+
+
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
+{
+ if (!hapd->dpp_auth)
+ return;
+
+ eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
+ eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+ hostapd_drv_send_action_cancel_wait(hapd);
+
+ if (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(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+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;
+ int ret = -1;
+ char *curve = NULL;
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ return -1;
+
+ curve = get_param(cmd, " curve=");
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 &&
+ dpp_configurator_own_config(auth, curve, 1) == 0) {
+ hostapd_dpp_handle_config_obj(hapd, auth);
+ ret = 0;
+ }
+
+ dpp_auth_deinit(auth);
+ os_free(curve);
+
+ return ret;
+}
+
+
+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;
+ const char *pos, *end;
+
+ pos = os_strstr(cmd, " own=");
+ if (!pos)
+ return -1;
+ pos += 5;
+ own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+ if (!own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not found");
+ return -1;
+ }
+ if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Identified bootstrap info not for PKEX");
+ return -1;
+ }
+ hapd->dpp_pkex_bi = own_bi;
+ own_bi->pkex_t = 0; /* clear pending errors on new code */
+
+ os_free(hapd->dpp_pkex_identifier);
+ hapd->dpp_pkex_identifier = NULL;
+ pos = os_strstr(cmd, " identifier=");
+ if (pos) {
+ pos += 12;
+ end = os_strchr(pos, ' ');
+ if (!end)
+ return -1;
+ hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
+ if (!hapd->dpp_pkex_identifier)
+ return -1;
+ os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
+ hapd->dpp_pkex_identifier[end - pos] = '\0';
+ }
+
+ pos = os_strstr(cmd, " code=");
+ if (!pos)
+ return -1;
+ os_free(hapd->dpp_pkex_code);
+ hapd->dpp_pkex_code = os_strdup(pos + 6);
+ if (!hapd->dpp_pkex_code)
+ return -1;
+
+ if (os_strstr(cmd, " init=1")) {
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi,
+ hapd->own_addr,
+ hapd->dpp_pkex_identifier,
+ hapd->dpp_pkex_code);
+ if (!hapd->dpp_pkex)
+ return -1;
+
+ msg = hapd->dpp_pkex->exchange_req;
+ /* TODO: Which channel to use? */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(broadcast), 2437,
+ DPP_PA_PKEX_EXCHANGE_REQ);
+ hostapd_drv_send_action(hapd, 2437, 0, broadcast,
+ wpabuf_head(msg), wpabuf_len(msg));
+ }
+
+ /* TODO: Support multiple PKEX info entries */
+
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
+
+ return 1;
+}
+
+
+int hostapd_dpp_pkex_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;
+ }
+
+ if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
+ return -1;
+
+ /* TODO: Support multiple PKEX entries */
+ os_free(hapd->dpp_pkex_code);
+ hapd->dpp_pkex_code = NULL;
+ os_free(hapd->dpp_pkex_identifier);
+ hapd->dpp_pkex_identifier = NULL;
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = NULL;
+ hapd->dpp_pkex_bi = NULL;
+ /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ return 0;
+}
+
+
+void hostapd_dpp_stop(struct hostapd_data *hapd)
+{
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+}
+
+
+int hostapd_dpp_init(struct hostapd_data *hapd)
+{
+ hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
+ hapd->dpp_init_done = 1;
+ return 0;
+}
+
+
+void hostapd_dpp_deinit(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(hapd->dpp_config_obj_override);
+ hapd->dpp_config_obj_override = NULL;
+ os_free(hapd->dpp_discovery_override);
+ hapd->dpp_discovery_override = NULL;
+ os_free(hapd->dpp_groups_override);
+ hapd->dpp_groups_override = NULL;
+ hapd->dpp_ignore_netaccesskey_mismatch = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!hapd->dpp_init_done)
+ return;
+ 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);
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ hostapd_dpp_pkex_remove(hapd, "*");
+ hapd->dpp_pkex = NULL;
+ 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
new file mode 100644
index 000000000000..3ef7c14567e8
--- /dev/null
+++ b/src/ap/dpp_hostapd.h
@@ -0,0 +1,43 @@
+/*
+ * hostapd / DPP integration
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_HOSTAPD_H
+#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);
+void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
+ const u8 *buf, size_t len, unsigned int freq);
+void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t data_len, int ok);
+struct wpabuf *
+hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
+ const u8 *query, size_t query_len);
+void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
+int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id);
+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);
+int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd);
+int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id);
+void hostapd_dpp_stop(struct hostapd_data *hapd);
+int hostapd_dpp_init(struct hostapd_data *hapd);
+void hostapd_dpp_deinit(struct hostapd_data *hapd);
+void hostapd_dpp_init_global(struct hapd_interfaces *ifaces);
+void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces);
+
+#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 3552b3e0d53b..a726a6ff87e0 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -31,10 +31,74 @@
#include "wps_hostapd.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "ap_mlme.h"
#include "hw_features.h"
#include "dfs.h"
#include "beacon.h"
#include "mbo_ap.h"
+#include "dpp_hostapd.h"
+#include "fils_hlp.h"
+
+
+#ifdef CONFIG_FILS
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ u16 reply_res = WLAN_STATUS_SUCCESS;
+ struct ieee802_11_elems elems;
+ u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
+ int new_assoc;
+
+ wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
+ __func__, MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ if (!sta->fils_pending_assoc_req)
+ return;
+
+ ieee802_11_parse_elems(sta->fils_pending_assoc_req,
+ sta->fils_pending_assoc_req_len, &elems, 0);
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element",
+ __func__);
+ return;
+ }
+
+ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+ elems.fils_session,
+ sta->fils_hlp_resp);
+
+ reply_res = hostapd_sta_assoc(hapd, sta->addr,
+ sta->fils_pending_assoc_is_reassoc,
+ WLAN_STATUS_SUCCESS,
+ buf, p - buf);
+ ap_sta_set_authorized(hapd, sta, 1);
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+ hostapd_set_sta_flags(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+ hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+ fils_hlp_deinit(hapd);
+
+ /*
+ * Remove the station in case transmission of a success response fails
+ * (the STA was added associated to the driver) or if the station was
+ * previously added unassociated.
+ */
+ if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ sta->added_unassoc = 0;
+ }
+}
+#endif /* CONFIG_FILS */
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -45,10 +109,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
struct ieee802_11_elems elems;
const u8 *ie;
size_t ielen;
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
u8 *p = buf;
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */
u16 reason = WLAN_REASON_UNSPECIFIED;
u16 status = WLAN_STATUS_SUCCESS;
const u8 *p2p_dev_addr = NULL;
@@ -171,6 +235,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
elems.hs20_len - 4);
} else
sta->hs20_ie = NULL;
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems.roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems.roaming_cons_sel + 4,
+ elems.roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
@@ -198,7 +270,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
#endif /* CONFIG_WPS */
wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
- return -1;
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ goto fail;
}
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
@@ -231,7 +305,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
ie, ielen,
- elems.mdie, elems.mdie_len);
+ elems.mdie, elems.mdie_len,
+ elems.owe_dh, elems.owe_dh_len);
if (res != WPA_IE_OK) {
wpa_printf(MSG_DEBUG,
"WPA/RSN information element rejected? (res %u)",
@@ -252,8 +327,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
} else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
- reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
- status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ reason = WLAN_REASON_CIPHER_SUITE_REJECTED;
+ status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
#endif /* CONFIG_IEEE80211W */
else {
@@ -263,10 +338,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
goto fail;
}
#ifdef CONFIG_IEEE80211W
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
sta->sa_query_count > 0)
ap_check_sa_query_timeout(hapd, sta);
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
(sta->auth_alg != WLAN_AUTH_FT)) {
/*
* STA has already been associated with MFP and SA
@@ -293,7 +372,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta->flags &= ~WLAN_STA_MFP;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
req_ies_len);
@@ -307,7 +386,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
goto fail;
}
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
} else if (hapd->conf->wps_state) {
#ifdef CONFIG_WPS
struct wpabuf *wps;
@@ -375,19 +454,128 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
skip_wpa_check:
#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
sta->auth_alg, req_ies, req_ies_len);
+ if (!p) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ int delay_assoc = 0;
+
+ if (!req_ies)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies,
+ req_ies_len,
+ sta->fils_session)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Session validation failed");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies,
+ req_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Key Confirm validation failed");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Delaying Assoc Response (HLP)");
+ delay_assoc = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Going ahead with Assoc Response (no HLP)");
+ }
+
+ if (sta) {
+ wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup");
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ sta->fils_drv_assoc_finish = 0;
+ }
+
+ if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) {
+ u8 *req_tmp;
+
+ req_tmp = os_malloc(req_ies_len);
+ if (!req_tmp) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: buffer allocation failed for assoc req");
+ goto fail;
+ }
+ os_memcpy(req_tmp, req_ies, req_ies_len);
+ sta->fils_pending_assoc_req = req_tmp;
+ sta->fils_pending_assoc_req_len = req_ies_len;
+ sta->fils_pending_assoc_is_reassoc = reassoc;
+ sta->fils_drv_assoc_finish = 1;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ eloop_register_timeout(
+ 0, hapd->conf->fils_hlp_wait_time * 1024,
+ fils_hlp_timeout, hapd, sta);
+ return 0;
+ }
+ p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p,
+ elems.fils_session,
+ sta->fils_hlp_resp);
+ wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)",
+ buf, p - buf);
+ }
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems.owe_dh) {
+ u8 *npos;
+
+ npos = owe_assoc_req_process(hapd, sta,
+ elems.owe_dh, elems.owe_dh_len,
+ p, sizeof(buf) - (p - buf),
+ &reason);
+ if (npos)
+ p = npos;
+ if (!npos &&
+ reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
+ status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+ p - buf);
+ return 0;
+ }
+
+ if (!npos || reason != WLAN_STATUS_SUCCESS)
+ goto fail;
+ }
+#endif /* CONFIG_OWE */
+
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
- if (sta->auth_alg == WLAN_AUTH_FT)
+ if (sta->auth_alg == WLAN_AUTH_FT ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)
ap_sta_set_authorized(hapd, sta, 1);
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
/* Keep compiler silent about unused variables */
if (status) {
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
@@ -397,6 +585,12 @@ skip_wpa_check:
if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+#ifdef CONFIG_FILS
+ else if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
+#endif /* CONFIG_FILS */
else
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
@@ -414,9 +608,9 @@ skip_wpa_check:
return 0;
fail:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
ap_free_sta(hapd, sta);
return -1;
@@ -464,15 +658,81 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
- if (!sta || !hapd->conf->disassoc_low_ack)
+ if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer)
return;
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"disconnected due to excessive missing ACKs");
hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
- if (sta)
- ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+ ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+ enum smps_mode smps_mode,
+ enum chan_width chan_width, u8 rx_nss)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ const char *txt;
+
+ if (!sta)
+ return;
+
+ switch (smps_mode) {
+ case SMPS_AUTOMATIC:
+ txt = "automatic";
+ break;
+ case SMPS_OFF:
+ txt = "off";
+ break;
+ case SMPS_DYNAMIC:
+ txt = "dynamic";
+ break;
+ case SMPS_STATIC:
+ txt = "static";
+ break;
+ default:
+ txt = NULL;
+ break;
+ }
+ if (txt) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED
+ MACSTR " %s", MAC2STR(addr), txt);
+ }
+
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ txt = "20(no-HT)";
+ break;
+ case CHAN_WIDTH_20:
+ txt = "20";
+ break;
+ case CHAN_WIDTH_40:
+ txt = "40";
+ break;
+ case CHAN_WIDTH_80:
+ txt = "80";
+ break;
+ case CHAN_WIDTH_80P80:
+ txt = "80+80";
+ break;
+ case CHAN_WIDTH_160:
+ txt = "160";
+ break;
+ default:
+ txt = NULL;
+ break;
+ }
+ if (txt) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED
+ MACSTR " %s", MAC2STR(addr), txt);
+ }
+
+ if (rx_nss != 0xff) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED
+ MACSTR " %d", MAC2STR(addr), rx_nss);
+ }
}
@@ -485,9 +745,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
- freq, ht, offset, width, channel_width_to_string(width),
- cf1, cf2);
+ "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+ freq, ht, hapd->iconf->ch_switch_vht_config, offset,
+ width, channel_width_to_string(width), cf1, cf2);
hapd->iface->freq = freq;
@@ -532,14 +792,26 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
- if (!ht)
+ if (!ht) {
hapd->iconf->ieee80211ac = 0;
+ } else if (hapd->iconf->ch_switch_vht_config) {
+ /* CHAN_SWITCH VHT config */
+ if (hapd->iconf->ch_switch_vht_config &
+ CH_SWITCH_VHT_ENABLED)
+ hapd->iconf->ieee80211ac = 1;
+ else if (hapd->iconf->ch_switch_vht_config &
+ CH_SWITCH_VHT_DISABLED)
+ hapd->iconf->ieee80211ac = 0;
+ }
+ hapd->iconf->ch_switch_vht_config = 0;
+
hapd->iconf->secondary_channel = offset;
hapd->iconf->vht_oper_chwidth = chwidth;
hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
- is_dfs = ieee80211_is_dfs(freq);
+ is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features,
+ hapd->iface->num_hw_features);
if (hapd->csa_in_progress &&
freq == hapd->cs_freq_params.freq) {
@@ -690,7 +962,7 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
#ifdef HOSTAPD
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
const u8 *bssid,
u16 auth_transaction, u16 status,
@@ -709,7 +981,33 @@ static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+#ifdef CONFIG_FILS
+static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub)
+{
+ if (resp == WLAN_STATUS_SUCCESS) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_FILS_SK;
+ mlme_authenticate_indication(hapd, sta);
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication failed (FILS)");
+ }
+
+ hostapd_sta_auth(hapd, sta->addr, 2, resp,
+ data ? wpabuf_head(data) : NULL,
+ data ? wpabuf_len(data) : 0);
+ wpabuf_free(data);
+}
+#endif /* CONFIG_FILS */
static void hostapd_notif_auth(struct hostapd_data *hapd,
@@ -730,7 +1028,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
}
sta->flags &= ~WLAN_STA_PREAUTH;
ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
@@ -748,7 +1046,19 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
hostapd_notify_auth_ft_finish, hapd);
return;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) {
+ sta->auth_alg = WLAN_AUTH_FILS_SK;
+ handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len,
+ rx_auth->auth_type, rx_auth->auth_transaction,
+ rx_auth->status_code,
+ hostapd_notify_auth_fils_finish);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
fail:
hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
status, resp_ies, resp_ies_len);
@@ -762,32 +1072,36 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
struct sta_info *sta;
size_t plen __maybe_unused;
u16 fc;
+ u8 *action __maybe_unused;
- if (drv_mgmt->frame_len < 24 + 1)
+ if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
return;
- plen = drv_mgmt->frame_len - 24 - 1;
+ plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1;
mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
fc = le_to_host16(mgmt->frame_control);
if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
return; /* handled by the driver */
- wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
- mgmt->u.action.category, (int) plen);
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " plen %d",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen);
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
return;
}
-#ifdef CONFIG_IEEE80211R
+#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);
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
ieee802_11_sa_query_action(
@@ -796,18 +1110,34 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
mgmt->u.action.u.sa_query_resp.trans_id);
}
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
if (mgmt->u.action.category == WLAN_ACTION_WNM) {
ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_FST
if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
return;
}
#endif /* CONFIG_FST */
+#ifdef CONFIG_DPP
+ if (plen >= 1 + 4 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+ pos = mgmt->u.action.u.vs_public_action.oui;
+ end = drv_mgmt->frame + drv_mgmt->frame_len;
+ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+ drv_mgmt->freq);
+ return;
+ }
+#endif /* CONFIG_DPP */
}
@@ -891,6 +1221,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
}
os_memset(&fi, 0, sizeof(fi));
+ fi.freq = rx_mgmt->freq;
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal;
@@ -1122,6 +1453,16 @@ static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
}
+static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq);
+ hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
struct dfs_event *radar)
{
@@ -1164,6 +1505,28 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
#endif /* NEED_AP_MLME */
+static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd,
+ int istatus,
+ const char *ifname,
+ const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (sta) {
+ os_free(sta->ifname_wds);
+ if (istatus == INTERFACE_ADDED)
+ sta->ifname_wds = os_strdup(ifname);
+ else
+ sta->ifname_wds = NULL;
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR,
+ istatus == INTERFACE_ADDED ?
+ WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED,
+ ifname, MAC2STR(addr));
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -1314,6 +1677,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
break;
+ case EVENT_DFS_PRE_CAC_EXPIRED:
+ if (!data)
+ break;
+ hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
+ break;
case EVENT_DFS_CAC_FINISHED:
if (!data)
break;
@@ -1351,7 +1719,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
* Try to re-enable interface if the driver stopped it
* when the interface got disabled.
*/
- wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+ if (hapd->wpa_auth)
+ wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+ else
+ hostapd_reconfig_encryption(hapd);
hapd->reenable_beacon = 1;
ieee802_11_set_beacon(hapd);
}
@@ -1367,6 +1738,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
&data->acs_selected_channels);
break;
#endif /* CONFIG_ACS */
+ case EVENT_STATION_OPMODE_CHANGED:
+ hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr,
+ data->sta_opmode.smps_mode,
+ data->sta_opmode.chan_width,
+ data->sta_opmode.rx_nss);
+ break;
+ case EVENT_WDS_STA_INTERFACE_STATUS:
+ hostapd_event_wds_sta_interface_status(
+ hapd, data->wds_sta_interface.istatus,
+ data->wds_sta_interface.ifname,
+ data->wds_sta_interface.sta_addr);
+ break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 082d0f53175e..296d5c2ddf31 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -91,6 +91,8 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
set_user_methods(user, argv[i]);
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
user->remediation = strlen(argv[i]) > 0;
+ } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
+ user->t_c_timestamp = strtol(argv[i], NULL, 10);
}
}
diff --git a/src/ap/eth_p_oui.c b/src/ap/eth_p_oui.c
new file mode 100644
index 000000000000..aba901e3fa75
--- /dev/null
+++ b/src/ap/eth_p_oui.c
@@ -0,0 +1,191 @@
+/*
+ * hostapd / IEEE 802 OUI Extended EtherType 88-B7
+ * Copyright (c) 2016, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "eth_p_oui.h"
+
+/*
+ * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended
+ * EtherType 88-B7. This file implements this with OUI 00:13:74 and
+ * vendor-specific subtype 0x0001.
+ */
+static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 };
+
+struct eth_p_oui_iface {
+ struct dl_list list;
+ char ifname[IFNAMSIZ + 1];
+ struct l2_packet_data *l2;
+ struct dl_list receiver;
+};
+
+struct eth_p_oui_ctx {
+ struct dl_list list;
+ struct eth_p_oui_iface *iface;
+ /* all data needed to deliver and unregister */
+ u8 oui_suffix; /* last byte of OUI */
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+};
+
+
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len)
+{
+ ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr,
+ ctx->oui_suffix, buf, len);
+}
+
+
+static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct eth_p_oui_iface *iface = ctx;
+ struct eth_p_oui_ctx *receiver;
+ const struct l2_ethhdr *ethhdr;
+
+ if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) {
+ /* too short packet */
+ return;
+ }
+
+ ethhdr = (struct l2_ethhdr *) buf;
+ /* trim eth_hdr from buf and len */
+ buf += sizeof(*ethhdr);
+ len -= sizeof(*ethhdr);
+
+ /* verify OUI and vendor-specific subtype match */
+ if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0)
+ return;
+ buf += sizeof(global_oui);
+ len -= sizeof(global_oui);
+
+ dl_list_for_each(receiver, &iface->receiver,
+ struct eth_p_oui_ctx, list) {
+ if (buf[0] != receiver->oui_suffix)
+ continue;
+
+ eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest,
+ buf + 1, len - 1);
+ }
+}
+
+
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx)
+{
+ struct eth_p_oui_iface *iface;
+ struct eth_p_oui_ctx *receiver;
+ int found = 0;
+ struct hapd_interfaces *interfaces;
+
+ receiver = os_zalloc(sizeof(*receiver));
+ if (!receiver)
+ goto err;
+
+ receiver->oui_suffix = oui_suffix;
+ receiver->rx_callback = rx_callback;
+ receiver->rx_callback_ctx = rx_callback_ctx;
+
+ interfaces = hapd->iface->interfaces;
+
+ dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface,
+ list) {
+ if (os_strcmp(iface->ifname, ifname) != 0)
+ continue;
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ iface = os_zalloc(sizeof(*iface));
+ if (!iface)
+ goto err;
+
+ os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname));
+ iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx,
+ iface, 1);
+ if (!iface->l2) {
+ os_free(iface);
+ goto err;
+ }
+ dl_list_init(&iface->receiver);
+
+ dl_list_add_tail(&interfaces->eth_p_oui, &iface->list);
+ }
+
+ dl_list_add_tail(&iface->receiver, &receiver->list);
+ receiver->iface = iface;
+
+ return receiver;
+err:
+ os_free(receiver);
+ return NULL;
+}
+
+
+void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx)
+{
+ struct eth_p_oui_iface *iface;
+
+ if (!ctx)
+ return;
+
+ iface = ctx->iface;
+
+ dl_list_del(&ctx->list);
+ os_free(ctx);
+
+ if (dl_list_empty(&iface->receiver)) {
+ dl_list_del(&iface->list);
+ l2_packet_deinit(iface->l2);
+ os_free(iface);
+ }
+}
+
+
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len)
+{
+ struct eth_p_oui_iface *iface = ctx->iface;
+ u8 *packet, *p;
+ size_t packet_len;
+ int ret;
+ struct l2_ethhdr *ethhdr;
+
+ packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len;
+ packet = os_zalloc(packet_len);
+ if (!packet)
+ return -1;
+ p = packet;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN);
+ os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+ ethhdr->h_proto = host_to_be16(ETH_P_OUI);
+ p += sizeof(*ethhdr);
+
+ os_memcpy(p, global_oui, sizeof(global_oui));
+ p[sizeof(global_oui)] = ctx->oui_suffix;
+ p += sizeof(global_oui) + 1;
+
+ os_memcpy(p, buf, len);
+
+ ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len);
+ os_free(packet);
+ return ret;
+}
diff --git a/src/ap/eth_p_oui.h b/src/ap/eth_p_oui.h
new file mode 100644
index 000000000000..466fdc39c6f8
--- /dev/null
+++ b/src/ap/eth_p_oui.h
@@ -0,0 +1,28 @@
+/*
+ * hostapd / IEEE 802 OUI Extended Ethertype
+ * Copyright (c) 2016, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ETH_P_OUI_H
+#define ETH_P_OUI_H
+
+struct eth_p_oui_ctx;
+struct hostapd_data;
+
+/* rx_callback only gets payload after OUI passed as buf */
+struct eth_p_oui_ctx *
+eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx);
+void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui);
+int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len);
+void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr,
+ const u8 *dst_addr, const u8 *buf, size_t len);
+
+#endif /* ETH_P_OUI_H */
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
new file mode 100644
index 000000000000..2a359ab03c81
--- /dev/null
+++ b/src/ap/fils_hlp.c
@@ -0,0 +1,641 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dhcp.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+#include "fils_hlp.h"
+
+
+static be16 ip_checksum(const void *buf, size_t len)
+{
+ u32 sum = 0;
+ const u16 *pos;
+
+ for (pos = buf; len >= 2; len -= 2)
+ sum += ntohs(*pos++);
+ if (len)
+ sum += ntohs(*pos << 8);
+
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += sum >> 16;
+ return htons(~sum);
+}
+
+
+static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
+ struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
+{
+ u8 *pos, *end;
+ struct dhcp_data *dhcp;
+ struct sockaddr_in addr;
+ ssize_t res;
+ const u8 *server_id = NULL;
+
+ if (!sta->hlp_dhcp_discover) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No pending HLP DHCPDISCOVER available");
+ return -1;
+ }
+
+ /* Convert to DHCPREQUEST, remove rapid commit option, replace requested
+ * IP address option with yiaddr. */
+ pos = wpabuf_mhead(sta->hlp_dhcp_discover);
+ end = pos + wpabuf_len(sta->hlp_dhcp_discover);
+ dhcp = (struct dhcp_data *) pos;
+ pos = (u8 *) (dhcp + 1);
+ pos += 4; /* skip magic */
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_MSG_TYPE:
+ if (olen > 0)
+ *pos = DHCPREQUEST;
+ break;
+ case DHCP_OPT_RAPID_COMMIT:
+ case DHCP_OPT_REQUESTED_IP_ADDRESS:
+ case DHCP_OPT_SERVER_ID:
+ /* Remove option */
+ pos -= 2;
+ os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
+ end -= 2 + olen;
+ olen = 0;
+ break;
+ }
+ pos += olen;
+ }
+ if (pos >= end || *pos != DHCP_OPT_END) {
+ wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
+ return -1;
+ }
+ sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
+
+ /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
+ pos = (u8 *) (dhcpoffer + 1);
+ end = dhcpofferend;
+ pos += 4; /* skip magic */
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_SERVER_ID:
+ server_id = pos - 2;
+ break;
+ }
+ pos += olen;
+ }
+
+ if (wpabuf_resize(&sta->hlp_dhcp_discover,
+ 6 + 1 + (server_id ? 2 + server_id[1] : 0)))
+ return -1;
+ if (server_id)
+ wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
+ 2 + server_id[1]);
+ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
+ wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
+ wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
+ wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+ addr.sin_port = htons(hapd->conf->dhcp_server_port);
+ res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
+ wpabuf_len(sta->hlp_dhcp_discover), 0,
+ (const struct sockaddr *) &addr, sizeof(addr));
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+ strerror(errno));
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FILS: Acting as DHCP rapid commit proxy for %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+ sta->fils_dhcp_rapid_commit_proxy = 1;
+ return 0;
+}
+
+
+static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct hostapd_data *hapd = sock_ctx;
+ struct sta_info *sta;
+ u8 buf[1500], *pos, *end, *end_opt = NULL;
+ struct dhcp_data *dhcp;
+ struct sockaddr_in addr;
+ socklen_t addr_len;
+ ssize_t res;
+ u8 msgtype = 0;
+ int rapid_commit = 0;
+ struct iphdr *iph;
+ struct udphdr *udph;
+ struct wpabuf *resp;
+ const u8 *rpos;
+ size_t left, len;
+
+ addr_len = sizeof(addr);
+ res = recvfrom(sd, buf, sizeof(buf), 0,
+ (struct sockaddr *) &addr, &addr_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
+ strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
+ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
+ if ((size_t) res < sizeof(*dhcp))
+ return;
+ dhcp = (struct dhcp_data *) buf;
+ if (dhcp->op != 2)
+ return; /* Not a BOOTREPLY */
+ if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - DHCP response to unknown relay address 0x%x",
+ dhcp->relay_ip);
+ return;
+ }
+ dhcp->relay_ip = 0;
+ pos = (u8 *) (dhcp + 1);
+ end = &buf[res];
+
+ if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
+ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
+ return;
+ }
+ pos += 4;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
+ pos, end - pos);
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_MSG_TYPE:
+ if (olen > 0)
+ msgtype = pos[0];
+ break;
+ case DHCP_OPT_RAPID_COMMIT:
+ rapid_commit = 1;
+ break;
+ }
+ pos += olen;
+ }
+ if (pos < end && *pos == DHCP_OPT_END)
+ end_opt = pos;
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
+ MACSTR ")",
+ msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
+
+ sta = ap_get_sta(hapd, dhcp->hw_addr);
+ if (!sta || !sta->fils_pending_assoc_req) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No pending HLP DHCP exchange with hw_addr "
+ MACSTR, MAC2STR(dhcp->hw_addr));
+ return;
+ }
+
+ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
+ !rapid_commit) {
+ /* Use hostapd to take care of 4-message exchange and convert
+ * the final DHCPACK to rapid commit version. */
+ if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
+ return;
+ /* failed, so send the server response as-is */
+ } else if (msgtype != DHCPACK) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
+ }
+
+ pos = buf;
+ resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
+ sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
+ if (!resp)
+ return;
+ wpabuf_put_data(resp, sta->addr, ETH_ALEN);
+ wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
+ wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
+ wpabuf_put_be16(resp, ETH_P_IP);
+ iph = wpabuf_put(resp, sizeof(*iph));
+ iph->version = 4;
+ iph->ihl = sizeof(*iph) / 4;
+ iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
+ iph->ttl = 1;
+ iph->protocol = 17; /* UDP */
+ iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
+ iph->daddr = dhcp->client_ip;
+ iph->check = ip_checksum(iph, sizeof(*iph));
+ udph = wpabuf_put(resp, sizeof(*udph));
+ udph->uh_sport = htons(DHCP_SERVER_PORT);
+ udph->uh_dport = htons(DHCP_CLIENT_PORT);
+ udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
+ udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
+ if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
+ !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
+ /* Add rapid commit option */
+ wpabuf_put_data(resp, pos, end_opt - pos);
+ wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
+ wpabuf_put_u8(resp, 0);
+ wpabuf_put_data(resp, end_opt, end - end_opt);
+ } else {
+ wpabuf_put_data(resp, pos, end - pos);
+ }
+ if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
+ 2 * wpabuf_len(resp) / 255 + 100)) {
+ wpabuf_free(resp);
+ return;
+ }
+
+ rpos = wpabuf_head(resp);
+ left = wpabuf_len(resp);
+
+ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
+ if (left <= 254)
+ len = 1 + left;
+ else
+ len = 255;
+ wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+ /* Destination MAC Address, Source MAC Address, HLP Packet.
+ * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
+ * when LPD is used). */
+ wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
+ rpos += len - 1;
+ left -= len - 1;
+ while (left) {
+ wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
+ len = left > 255 ? 255 : left;
+ wpabuf_put_u8(sta->fils_hlp_resp, len);
+ wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
+ rpos += len;
+ left -= len;
+ }
+ wpabuf_free(resp);
+
+ if (sta->fils_drv_assoc_finish)
+ hostapd_notify_assoc_fils_finish(hapd, sta);
+ else
+ fils_hlp_finish_assoc(hapd, sta);
+}
+
+
+static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *msg, size_t len)
+{
+ const struct dhcp_data *dhcp;
+ struct wpabuf *dhcp_buf;
+ struct dhcp_data *dhcp_msg;
+ u8 msgtype = 0;
+ int rapid_commit = 0;
+ const u8 *pos = msg, *end;
+ struct sockaddr_in addr;
+ ssize_t res;
+
+ if (len < sizeof(*dhcp))
+ return 0;
+ dhcp = (const struct dhcp_data *) pos;
+ end = pos + len;
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
+ dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
+ ntohl(dhcp->xid));
+ pos += sizeof(*dhcp);
+ if (dhcp->op != 1)
+ return 0; /* Not a BOOTREQUEST */
+
+ if (end - pos < 4)
+ return 0;
+ if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
+ wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
+ return 0;
+ }
+ pos += 4;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
+ while (pos < end && *pos != DHCP_OPT_END) {
+ u8 opt, olen;
+
+ opt = *pos++;
+ if (opt == DHCP_OPT_PAD)
+ continue;
+ if (pos >= end)
+ break;
+ olen = *pos++;
+ if (olen > end - pos)
+ break;
+
+ switch (opt) {
+ case DHCP_OPT_MSG_TYPE:
+ if (olen > 0)
+ msgtype = pos[0];
+ break;
+ case DHCP_OPT_RAPID_COMMIT:
+ rapid_commit = 1;
+ break;
+ }
+ pos += olen;
+ }
+
+ wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
+ if (msgtype != DHCPDISCOVER)
+ return 0;
+
+ if (hapd->conf->dhcp_server.af != AF_INET ||
+ hapd->conf->dhcp_server.u.v4.s_addr == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - no DHCPv4 server configured - drop request");
+ return 0;
+ }
+
+ if (hapd->conf->own_ip_addr.af != AF_INET ||
+ hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
+ return 0;
+ }
+
+ if (hapd->dhcp_sock < 0) {
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to open DHCP socket: %s",
+ strerror(errno));
+ return 0;
+ }
+
+ if (hapd->conf->dhcp_relay_port) {
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr =
+ hapd->conf->own_ip_addr.u.v4.s_addr;
+ addr.sin_port = htons(hapd->conf->dhcp_relay_port);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to bind DHCP socket: %s",
+ strerror(errno));
+ close(s);
+ return 0;
+ }
+ }
+ if (eloop_register_sock(s, EVENT_TYPE_READ,
+ fils_dhcp_handler, NULL, hapd)) {
+ close(s);
+ return 0;
+ }
+
+ hapd->dhcp_sock = s;
+ }
+
+ dhcp_buf = wpabuf_alloc(len);
+ if (!dhcp_buf)
+ return 0;
+ dhcp_msg = wpabuf_put(dhcp_buf, len);
+ os_memcpy(dhcp_msg, msg, len);
+ dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+ addr.sin_port = htons(hapd->conf->dhcp_server_port);
+ res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
+ (const struct sockaddr *) &addr, sizeof(addr));
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+ strerror(errno));
+ wpabuf_free(dhcp_buf);
+ /* Close the socket to try to recover from error */
+ eloop_unregister_read_sock(hapd->dhcp_sock);
+ close(hapd->dhcp_sock);
+ hapd->dhcp_sock = -1;
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+ rapid_commit);
+ if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
+ /* Store a copy of the DHCPDISCOVER for rapid commit proxying
+ * purposes if the server does not support the rapid commit
+ * option. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Store DHCPDISCOVER for rapid commit proxy");
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = dhcp_buf;
+ } else {
+ wpabuf_free(dhcp_buf);
+ }
+
+ return 1;
+}
+
+
+static int fils_process_hlp_udp(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *dst,
+ const u8 *pos, size_t len)
+{
+ const struct iphdr *iph;
+ const struct udphdr *udph;
+ u16 sport, dport, ulen;
+
+ if (len < sizeof(*iph) + sizeof(*udph))
+ return 0;
+ iph = (const struct iphdr *) pos;
+ udph = (const struct udphdr *) (iph + 1);
+ sport = ntohs(udph->uh_sport);
+ dport = ntohs(udph->uh_dport);
+ ulen = ntohs(udph->uh_ulen);
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
+ sport, dport, ulen, ntohs(udph->uh_sum));
+ /* TODO: Check UDP checksum */
+ if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
+ return 0;
+
+ if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
+ return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
+ ulen - sizeof(*udph));
+ }
+
+ return 0;
+}
+
+
+static int fils_process_hlp_ip(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *dst,
+ const u8 *pos, size_t len)
+{
+ const struct iphdr *iph;
+ u16 tot_len;
+
+ if (len < sizeof(*iph))
+ return 0;
+ iph = (const struct iphdr *) pos;
+ if (ip_checksum(iph, sizeof(*iph)) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
+ return 0;
+ }
+ tot_len = ntohs(iph->tot_len);
+ if (tot_len > len)
+ return 0;
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
+ iph->saddr, iph->daddr, iph->protocol);
+ switch (iph->protocol) {
+ case 17:
+ return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+ }
+
+ return 0;
+}
+
+
+static int fils_process_hlp_req(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *pos, size_t len)
+{
+ const u8 *pkt, *end;
+
+ wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
+ " src=" MACSTR " len=%u)",
+ MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
+ (unsigned int) len);
+ if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Ignore HLP request with unexpected source address"
+ MACSTR, MAC2STR(pos + ETH_ALEN));
+ return 0;
+ }
+
+ end = pos + len;
+ pkt = pos + 2 * ETH_ALEN;
+ if (end - pkt >= 6 &&
+ os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
+ pkt += 6; /* Remove SNAP/LLC header */
+ wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
+
+ if (end - pkt < 2)
+ return 0;
+
+ switch (WPA_GET_BE16(pkt)) {
+ case ETH_P_IP:
+ return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
+ end - pkt - 2);
+ }
+
+ return 0;
+}
+
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, int left)
+{
+ const u8 *end = pos + left;
+ u8 *tmp, *tmp_pos;
+ int ret = 0;
+
+ /* Old DHCPDISCOVER is not needed anymore, if it was still pending */
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+ sta->fils_dhcp_rapid_commit_proxy = 0;
+
+ /* Check if there are any FILS HLP Container elements */
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos)
+ return 0;
+ if (pos[0] == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 + 2 * ETH_ALEN &&
+ pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
+ break;
+ pos += 2 + pos[1];
+ }
+ if (end - pos < 2)
+ return 0; /* No FILS HLP Container elements */
+
+ tmp = os_malloc(end - pos);
+ if (!tmp)
+ return 0;
+
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos ||
+ pos[0] != WLAN_EID_EXTENSION ||
+ pos[1] < 1 + 2 * ETH_ALEN ||
+ pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
+ break;
+ tmp_pos = tmp;
+ os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
+ tmp_pos += pos[1] - 1;
+ pos += 2 + pos[1];
+
+ /* Add possible fragments */
+ while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
+ 2 + pos[1] <= end - pos) {
+ os_memcpy(tmp_pos, pos + 2, pos[1]);
+ tmp_pos += pos[1];
+ pos += 2 + pos[1];
+ }
+
+ if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
+ ret = 1;
+ }
+
+ os_free(tmp);
+
+ return ret;
+}
+
+
+void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->dhcp_sock >= 0) {
+ eloop_unregister_read_sock(hapd->dhcp_sock);
+ close(hapd->dhcp_sock);
+ hapd->dhcp_sock = -1;
+ }
+}
diff --git a/src/ap/fils_hlp.h b/src/ap/fils_hlp.h
new file mode 100644
index 000000000000..e14a6bf65e04
--- /dev/null
+++ b/src/ap/fils_hlp.h
@@ -0,0 +1,27 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FILS_HLP_H
+#define FILS_HLP_H
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, int left);
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_FILS */
+
+static inline void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_FILS */
+
+#endif /* FILS_HLP_H */
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
new file mode 100644
index 000000000000..fdb3cad55ade
--- /dev/null
+++ b/src/ap/gas_query_ap.c
@@ -0,0 +1,714 @@
+/*
+ * Generic advertisement service (GAS) query (hostapd)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "gas_query_ap.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 2
+
+/* GAS query wait-time / duration in ms */
+#define GAS_QUERY_WAIT_TIME_INITIAL 1000
+#define GAS_QUERY_WAIT_TIME_COMEBACK 150
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+ struct dl_list list;
+ struct gas_query_ap *gas;
+ u8 addr[ETH_ALEN];
+ u8 dialog_token;
+ u8 next_frag_id;
+ unsigned int wait_comeback:1;
+ unsigned int offchannel_tx_started:1;
+ unsigned int retry:1;
+ int freq;
+ u16 status_code;
+ struct wpabuf *req;
+ struct wpabuf *adv_proto;
+ struct wpabuf *resp;
+ struct os_reltime last_oper;
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+ void *ctx;
+ u8 sa[ETH_ALEN];
+};
+
+/**
+ * struct gas_query_ap - Internal GAS query data
+ */
+struct gas_query_ap {
+ struct hostapd_data *hapd;
+ void *msg_ctx;
+ struct dl_list pending; /* struct gas_query_pending */
+ struct gas_query_pending *current;
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+ struct gas_query_pending *query);
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst);
+
+
+static int ms_from_time(struct os_reltime *last)
+{
+ struct os_reltime now, res;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, last, &res);
+ return res.sec * 1000 + res.usec / 1000;
+}
+
+
+/**
+ * gas_query_ap_init - Initialize GAS query component
+ * @hapd: Pointer to hostapd data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+ void *msg_ctx)
+{
+ struct gas_query_ap *gas;
+
+ gas = os_zalloc(sizeof(*gas));
+ if (!gas)
+ return NULL;
+
+ gas->hapd = hapd;
+ gas->msg_ctx = msg_ctx;
+ dl_list_init(&gas->pending);
+
+ return gas;
+}
+
+
+static const char * gas_result_txt(enum gas_query_ap_result result)
+{
+ switch (result) {
+ case GAS_QUERY_AP_SUCCESS:
+ return "SUCCESS";
+ case GAS_QUERY_AP_FAILURE:
+ return "FAILURE";
+ case GAS_QUERY_AP_TIMEOUT:
+ return "TIMEOUT";
+ case GAS_QUERY_AP_PEER_ERROR:
+ return "PEER_ERROR";
+ case GAS_QUERY_AP_INTERNAL_ERROR:
+ return "INTERNAL_ERROR";
+ case GAS_QUERY_AP_DELETED_AT_DEINIT:
+ return "DELETED_AT_DEINIT";
+ }
+
+ return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+ if (del_list)
+ dl_list_del(&query->list);
+
+ wpabuf_free(query->req);
+ wpabuf_free(query->adv_proto);
+ wpabuf_free(query->resp);
+ os_free(query);
+}
+
+
+static void gas_query_done(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ enum gas_query_ap_result result)
+{
+ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+ " dialog_token=%u freq=%d status_code=%u result=%s",
+ MAC2STR(query->addr), query->dialog_token, query->freq,
+ query->status_code, gas_result_txt(result));
+ if (gas->current == query)
+ gas->current = NULL;
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+ dl_list_del(&query->list);
+ query->cb(query->ctx, query->addr, query->dialog_token, result,
+ query->adv_proto, query->resp, query->status_code);
+ gas_query_free(query, 0);
+}
+
+
+/**
+ * gas_query_ap_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_ap_deinit(struct gas_query_ap *gas)
+{
+ struct gas_query_pending *query, *next;
+
+ if (gas == NULL)
+ return;
+
+ dl_list_for_each_safe(query, next, &gas->pending,
+ struct gas_query_pending, list)
+ gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT);
+
+ os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ q->dialog_token == dialog_token)
+ return q;
+ }
+ return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+ size_t len)
+{
+ if (wpabuf_resize(&query->resp, len) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+ return -1;
+ }
+ wpabuf_put_data(query->resp, data, len);
+ return 0;
+}
+
+
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+ const u8 *data, size_t data_len, int ok)
+{
+ struct gas_query_pending *query;
+ int dur;
+
+ if (!gas || !gas->current) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR
+ " ok=%d - no query in progress", MAC2STR(dst), ok);
+ return;
+ }
+
+ query = gas->current;
+
+ dur = ms_from_time(&query->last_oper);
+ wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
+ " ok=%d query=%p dialog_token=%u dur=%d ms",
+ MAC2STR(dst), ok, query, query->dialog_token, dur);
+ if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+ return;
+ }
+ os_get_reltime(&query->last_oper);
+
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ if (!ok) {
+ wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
+ eloop_register_timeout(0, 250000, gas_query_timeout,
+ gas, query);
+ } else {
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+ }
+ if (query->wait_comeback && !query->retry) {
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout,
+ gas, query);
+ eloop_register_timeout(
+ 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
+ gas_query_rx_comeback_timeout, gas, query);
+ }
+}
+
+
+static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ return sta && (sta->flags & WLAN_STA_MFP);
+}
+
+
+static int gas_query_tx(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ struct wpabuf *req, unsigned int wait_time)
+{
+ int res, prot = pmf_in_use(gas->hapd, query->addr);
+
+ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+ "freq=%d prot=%d using src addr " MACSTR,
+ MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
+ query->freq, prot, MAC2STR(query->sa));
+ if (prot) {
+ u8 *categ = wpabuf_mhead_u8(req);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+ }
+ os_get_reltime(&query->last_oper);
+ res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time,
+ query->addr, wpabuf_head(req),
+ wpabuf_len(req));
+ return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query_ap *gas,
+ struct gas_query_pending *query)
+{
+ struct wpabuf *req;
+ unsigned int wait_time;
+
+ req = gas_build_comeback_req(query->dialog_token);
+ if (req == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ wait_time = (query->retry || !query->offchannel_tx_started) ?
+ GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
+
+ if (gas_query_tx(gas, query, req, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ }
+
+ wpabuf_free(req);
+}
+
+
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query_ap *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+ int dialog_token;
+
+ wpa_printf(MSG_DEBUG,
+ "GAS: No response to comeback request received (retry=%u)",
+ query->retry);
+ if (gas->current != query || query->retry)
+ return;
+ dialog_token = gas_query_new_dialog_token(gas, query->addr);
+ if (dialog_token < 0)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "GAS: Retry GAS query due to comeback response timeout");
+ query->retry = 1;
+ query->dialog_token = dialog_token;
+ *(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
+ query->wait_comeback = 0;
+ query->next_frag_id = 0;
+ wpabuf_free(query->adv_proto);
+ query->adv_proto = NULL;
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ gas_query_tx_initial_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query_ap *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ u16 comeback_delay)
+{
+ unsigned int secs, usecs;
+
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+ gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+ MACSTR " (dialog_token=%u comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+ if (query->adv_proto == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ query->wait_comeback = 1;
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ /* Query was completed without comeback mechanism */
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query_ap *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u8 frag_id, u8 more_frags,
+ u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+ "comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, frag_id,
+ more_frags, comeback_delay);
+ eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
+
+ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+ os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+ wpabuf_len(query->adv_proto)) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+ "between initial and comeback response from "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ if (frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+ "with non-zero frag_id and comeback_delay "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+ return;
+ }
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ if (frag_id != query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+ "from " MACSTR, MAC2STR(query->addr));
+ if (frag_id + 1 == query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
+ "retry of previous fragment");
+ return;
+ }
+ gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR);
+ return;
+ }
+ query->next_frag_id++;
+
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+
+ if (more_frags) {
+ gas_query_tx_comeback_req(gas, query);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS);
+}
+
+
+/**
+ * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual
+ * frame
+ * @gas: GAS query data from gas_query_init()
+ * @sa: Source MAC address of the Action frame
+ * @categ: Category of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+ const u8 *data, size_t len, int freq)
+{
+ struct gas_query_pending *query;
+ u8 action, dialog_token, frag_id = 0, more_frags = 0;
+ u16 comeback_delay, resp_len;
+ const u8 *pos, *adv_proto;
+ int prot, pmf;
+ unsigned int left;
+
+ if (!gas || len < 4)
+ return -1;
+
+ pos = data;
+ action = *pos++;
+ dialog_token = *pos++;
+
+ if (action != WLAN_PA_GAS_INITIAL_RESP &&
+ action != WLAN_PA_GAS_COMEBACK_RESP)
+ return -1; /* Not a GAS response */
+
+ prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+ pmf = pmf_in_use(gas->hapd, sa);
+ if (prot && !pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+ return 0;
+ }
+ if (!prot && pmf) {
+ wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+ return 0;
+ }
+
+ query = gas_query_get_pending(gas, sa, dialog_token);
+ if (query == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+ " dialog token %u", MAC2STR(sa), dialog_token);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+ ms_from_time(&query->last_oper), MAC2STR(sa));
+
+ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+ MACSTR " dialog token %u when waiting for comeback "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+ MACSTR " dialog token %u when waiting for initial "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ query->status_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+ action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+ } else if (query->status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+ "%u failed - status code %u",
+ MAC2STR(sa), dialog_token, query->status_code);
+ gas_query_done(gas, query, GAS_QUERY_AP_FAILURE);
+ return 0;
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+ if (pos + 1 > data + len)
+ return 0;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ }
+
+ /* Comeback Delay */
+ if (pos + 2 > data + len)
+ return 0;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* Advertisement Protocol element */
+ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+ "Protocol element in the response from " MACSTR,
+ MAC2STR(sa));
+ return 0;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+ "Protocol element ID %u in response from " MACSTR,
+ *pos, MAC2STR(sa));
+ return 0;
+ }
+
+ adv_proto = pos;
+ pos += 2 + pos[1];
+
+ /* Query Response Length */
+ if (pos + 2 > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+ return 0;
+ }
+ resp_len = WPA_GET_LE16(pos);
+ pos += 2;
+
+ left = data + len - pos;
+ if (resp_len > left) {
+ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+ "response from " MACSTR, MAC2STR(sa));
+ return 0;
+ }
+
+ if (resp_len < left) {
+ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+ "after Query Response from " MACSTR,
+ left - resp_len, MAC2STR(sa));
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+ frag_id, more_frags, comeback_delay);
+ else
+ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+ comeback_delay);
+
+ return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query_ap *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+ " dialog token %u",
+ MAC2STR(query->addr), query->dialog_token);
+ gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query_ap *gas,
+ const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ dialog_token == q->dialog_token)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void gas_query_tx_initial_req(struct gas_query_ap *gas,
+ struct gas_query_pending *query)
+{
+ if (gas_query_tx(gas, query, query->req,
+ GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR);
+ return;
+ }
+ gas->current = query;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+ query->dialog_token);
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+ gas_query_timeout, gas, query);
+}
+
+
+static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst)
+{
+ static int next_start = 0;
+ int dialog_token;
+
+ for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+ if (gas_query_dialog_token_available(
+ gas, dst, (next_start + dialog_token) % 256))
+ break;
+ }
+ if (dialog_token == 256)
+ return -1; /* Too many pending queries */
+ dialog_token = (next_start + dialog_token) % 256;
+ next_start = (dialog_token + 1) % 256;
+ return dialog_token;
+}
+
+
+/**
+ * gas_query_ap_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ * return)
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx)
+{
+ struct gas_query_pending *query;
+ int dialog_token;
+
+ if (!gas || wpabuf_len(req) < 3)
+ return -1;
+
+ dialog_token = gas_query_new_dialog_token(gas, dst);
+ if (dialog_token < 0)
+ return -1;
+
+ query = os_zalloc(sizeof(*query));
+ if (query == NULL)
+ return -1;
+
+ query->gas = gas;
+ os_memcpy(query->addr, dst, ETH_ALEN);
+ query->dialog_token = dialog_token;
+ query->freq = freq;
+ query->cb = cb;
+ query->ctx = ctx;
+ query->req = req;
+ dl_list_add(&gas->pending, &query->list);
+
+ *(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+ wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+ " dialog_token=%u freq=%d",
+ MAC2STR(query->addr), query->dialog_token, query->freq);
+
+ gas_query_tx_initial_req(gas, query);
+
+ return dialog_token;
+}
diff --git a/src/ap/gas_query_ap.h b/src/ap/gas_query_ap.h
new file mode 100644
index 000000000000..70f1f0537657
--- /dev/null
+++ b/src/ap/gas_query_ap.h
@@ -0,0 +1,43 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2017, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_AP_H
+#define GAS_QUERY_AP_H
+
+struct gas_query_ap;
+
+struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd,
+ void *msg_ctx);
+void gas_query_ap_deinit(struct gas_query_ap *gas);
+int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ,
+ const u8 *data, size_t len, int freq);
+
+/**
+ * enum gas_query_ap_result - GAS query result
+ */
+enum gas_query_ap_result {
+ GAS_QUERY_AP_SUCCESS,
+ GAS_QUERY_AP_FAILURE,
+ GAS_QUERY_AP_TIMEOUT,
+ GAS_QUERY_AP_PEER_ERROR,
+ GAS_QUERY_AP_INTERNAL_ERROR,
+ GAS_QUERY_AP_DELETED_AT_DEINIT
+};
+
+int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_ap_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx);
+void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst,
+ const u8 *data, size_t data_len, int ok);
+
+#endif /* GAS_QUERY_AP_H */
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 6ce178de3b29..a7df81032477 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -11,14 +11,31 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/gas.h"
+#include "common/wpa_ctrl.h"
#include "utils/eloop.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "dpp_hostapd.h"
#include "sta_info.h"
#include "gas_serv.h"
+#ifdef CONFIG_DPP
+static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf)
+{
+ 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);
+}
+#endif /* CONFIG_DPP */
+
+
static void convert_to_protected_dual(struct wpabuf *msg)
{
u8 *categ = wpabuf_mhead_u8(msg);
@@ -50,9 +67,12 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
sta->flags |= WLAN_STA_GAS;
/*
* The default inactivity is 300 seconds. We don't need
- * it to be that long.
+ * it to be that long. Use five second timeout and increase this
+ * with the comeback_delay for testing cases.
*/
- ap_sta_session_timeout(hapd, sta, 5);
+ ap_sta_session_timeout(hapd, sta,
+ hapd->conf->gas_comeback_delay / 1024 +
+ 5);
} else {
ap_sta_replenish_timeout(hapd, sta, 5);
}
@@ -161,8 +181,12 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
if (hapd->conf->hs20_osu_providers_count)
wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ if (hapd->conf->hs20_osu_providers_nai_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
if (hapd->conf->hs20_icons_count)
wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+ if (hapd->conf->hs20_operator_icon_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
@@ -255,20 +279,29 @@ static void anqp_add_capab_list(struct hostapd_data *hapd,
wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+ if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
+ wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
- for (id = 273; id < 277; id++) {
- if (get_anqp_elem(hapd, id))
- wpabuf_put_le16(buf, id);
- }
- if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+#ifdef CONFIG_FILS
+ if (!dl_list_empty(&hapd->conf->fils_realms) ||
+ get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
+ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+#endif /* CONFIG_FILS */
+ if (get_anqp_elem(hapd, ANQP_CAG))
+ wpabuf_put_le16(buf, ANQP_CAG);
+ if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL))
wpabuf_put_le16(buf, ANQP_VENUE_URL);
if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
+ for (id = 280; id < 300; id++) {
+ if (get_anqp_elem(hapd, id))
+ wpabuf_put_le16(buf, id);
+ }
#ifdef CONFIG_HS20
anqp_add_hs_capab_list(hapd, buf);
#endif /* CONFIG_HS20 */
@@ -299,6 +332,29 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
}
+static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (anqp_add_override(hapd, buf, ANQP_VENUE_URL))
+ return;
+
+ if (hapd->conf->venue_url) {
+ u8 *len;
+ unsigned int i;
+
+ len = gas_anqp_add_element(buf, ANQP_VENUE_URL);
+ for (i = 0; i < hapd->conf->venue_url_count; i++) {
+ struct hostapd_venue_url *url;
+
+ url = &hapd->conf->venue_url[i];
+ wpabuf_put_u8(buf, 1 + url->url_len);
+ wpabuf_put_u8(buf, url->venue_number);
+ wpabuf_put_data(buf, url->url, url->url_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
static void anqp_add_network_auth_type(struct hostapd_data *hapd,
struct wpabuf *buf)
{
@@ -548,6 +604,36 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
}
+#ifdef CONFIG_FILS
+static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ size_t count;
+
+ if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
+ return;
+
+ count = dl_list_len(&hapd->conf->fils_realms);
+ if (count > 10000)
+ count = 10000;
+ if (count) {
+ struct fils_realm *realm;
+
+ wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+ wpabuf_put_le16(buf, 2 * count);
+
+ dl_list_for_each(realm, &hapd->conf->fils_realms,
+ struct fils_realm, list) {
+ if (count == 0)
+ break;
+ wpabuf_put_data(buf, realm->hash, 2);
+ count--;
+ }
+ }
+}
+#endif /* CONFIG_FILS */
+
+
#ifdef CONFIG_HS20
static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
@@ -621,6 +707,29 @@ static void anqp_add_operating_class(struct hostapd_data *hapd,
}
+static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss,
+ const char *name)
+{
+ size_t j;
+ struct hs20_icon *icon = NULL;
+
+ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+ if (os_strcmp(name, bss->hs20_icons[j].name) == 0)
+ icon = &bss->hs20_icons[j];
+ }
+ if (!icon)
+ return; /* icon info not found */
+
+ wpabuf_put_le16(buf, icon->width);
+ wpabuf_put_le16(buf, icon->height);
+ wpabuf_put_data(buf, icon->language, 3);
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_u8(buf, os_strlen(icon->name));
+ wpabuf_put_str(buf, icon->name);
+}
+
+
static void anqp_add_osu_provider(struct wpabuf *buf,
struct hostapd_bss_config *bss,
struct hs20_osu_provider *p)
@@ -649,32 +758,14 @@ static void anqp_add_osu_provider(struct wpabuf *buf,
/* OSU Method List */
count = wpabuf_put(buf, 1);
- for (i = 0; p->method_list[i] >= 0; i++)
+ for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
wpabuf_put_u8(buf, p->method_list[i]);
*count = i;
/* Icons Available */
len2 = wpabuf_put(buf, 2);
- for (i = 0; i < p->icons_count; i++) {
- size_t j;
- struct hs20_icon *icon = NULL;
-
- for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
- if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
- 0)
- icon = &bss->hs20_icons[j];
- }
- if (!icon)
- continue; /* icon info not found */
-
- wpabuf_put_le16(buf, icon->width);
- wpabuf_put_le16(buf, icon->height);
- wpabuf_put_data(buf, icon->language, 3);
- wpabuf_put_u8(buf, os_strlen(icon->type));
- wpabuf_put_str(buf, icon->type);
- wpabuf_put_u8(buf, os_strlen(icon->name));
- wpabuf_put_str(buf, icon->name);
- }
+ for (i = 0; i < p->icons_count; i++)
+ anqp_add_icon(buf, bss, p->icons[i]);
WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
/* OSU_NAI */
@@ -728,6 +819,40 @@ static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
}
+static void anqp_add_osu_provider_nai(struct wpabuf *buf,
+ struct hs20_osu_provider *p)
+{
+ /* OSU_NAI for shared BSS (Single SSID) */
+ if (p->osu_nai2) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai2));
+ wpabuf_put_str(buf, p->osu_nai2);
+ } else {
+ wpabuf_put_u8(buf, 0);
+ }
+}
+
+
+static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_nai_count) {
+ size_t i;
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider_nai(
+ buf, &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
struct wpabuf *buf,
const u8 *name, size_t name_len)
@@ -783,9 +908,49 @@ static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
gas_anqp_set_element_len(buf, len);
}
+
+static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ struct hostapd_bss_config *bss = hapd->conf;
+ size_t i;
+ u8 *len;
+
+ if (!bss->hs20_operator_icon_count)
+ return;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ for (i = 0; i < bss->hs20_operator_icon_count; i++)
+ anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]);
+
+ gas_anqp_set_element_len(buf, len);
+}
+
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->mbo_cell_data_conn_pref >= 0) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+ wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+#endif /* CONFIG_MBO */
+
+
static size_t anqp_get_required_len(struct hostapd_data *hapd,
const u16 *infoid,
unsigned int num_infoid)
@@ -821,6 +986,10 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
len += 1000;
if (request & ANQP_REQ_ICON_REQUEST)
len += 65536;
+#ifdef CONFIG_FILS
+ if (request & ANQP_FILS_REALM_INFO)
+ len += 2 * dl_list_len(&hapd->conf->fils_realms);
+#endif /* CONFIG_FILS */
len += anqp_get_required_len(hapd, extra_req, num_extra_req);
buf = wpabuf_alloc(len);
@@ -860,8 +1029,19 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
if (request & ANQP_REQ_EMERGENCY_NAI)
anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
- for (i = 0; i < num_extra_req; i++)
+ for (i = 0; i < num_extra_req; i++) {
+#ifdef CONFIG_FILS
+ if (extra_req[i] == ANQP_FILS_REALM_INFO) {
+ anqp_add_fils_realm_info(hapd, buf);
+ continue;
+ }
+#endif /* CONFIG_FILS */
+ if (extra_req[i] == ANQP_VENUE_URL) {
+ anqp_add_venue_url(hapd, buf);
+ continue;
+ }
anqp_add_elem(hapd, buf, extra_req[i]);
+ }
#ifdef CONFIG_HS20
if (request & ANQP_REQ_HS_CAPABILITY_LIST)
@@ -878,8 +1058,17 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
anqp_add_osu_providers_list(hapd, buf);
if (request & ANQP_REQ_ICON_REQUEST)
anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
+ if (request & ANQP_REQ_OPERATOR_ICON_METADATA)
+ anqp_add_operator_icon_metadata(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST)
+ anqp_add_osu_providers_nai_list(hapd, buf);
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF)
+ anqp_add_mbo_cell_data_conn_pref(hapd, buf);
+#endif /* CONFIG_MBO */
+
return buf;
}
@@ -984,7 +1173,17 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
get_anqp_elem(hapd, info_id) != NULL, qi);
break;
default:
- if (!get_anqp_elem(hapd, info_id)) {
+#ifdef CONFIG_FILS
+ if (info_id == ANQP_FILS_REALM_INFO &&
+ !dl_list_empty(&hapd->conf->fils_realms)) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: FILS Realm Information (local)");
+ } else
+#endif /* CONFIG_FILS */
+ if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Venue URL (local)");
+ } else if (!get_anqp_elem(hapd, info_id)) {
wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
info_id);
break;
@@ -1050,6 +1249,16 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
hapd->conf->hs20_osu_providers_count, qi);
break;
+ case HS20_STYPE_OPERATOR_ICON_METADATA:
+ set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA,
+ "Operator Icon Metadata",
+ hapd->conf->hs20_operator_icon_count, qi);
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_NAI_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST,
+ "OSU Providers NAI List",
+ hapd->conf->hs20_osu_providers_nai_count, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
subtype);
@@ -1092,49 +1301,12 @@ static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
}
-static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
- const u8 *pos, const u8 *end,
- struct anqp_query_info *qi)
+static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
{
- u32 oui;
u8 subtype;
- if (end - pos < 4) {
- wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
- "Query element");
- return;
- }
-
- oui = WPA_GET_BE24(pos);
- pos += 3;
- if (oui != OUI_WFA) {
- wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
- oui);
- return;
- }
-
-#ifdef CONFIG_P2P
- if (*pos == P2P_OUI_TYPE) {
- /*
- * This is for P2P SD and will be taken care of by the P2P
- * implementation. This query needs to be ignored in the generic
- * GAS server to avoid duplicated response.
- */
- wpa_printf(MSG_DEBUG,
- "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
- *pos);
- qi->p2p_sd = 1;
- return;
- }
-#endif /* CONFIG_P2P */
-
- if (*pos != HS20_ANQP_OUI_TYPE) {
- wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
- *pos);
- return;
- }
- pos++;
-
if (end - pos <= 1)
return;
@@ -1164,6 +1336,115 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_P2P
+static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd,
+ struct anqp_query_info *qi)
+{
+ /*
+ * This is for P2P SD and will be taken care of by the P2P
+ * implementation. This query needs to be ignored in the generic
+ * GAS server to avoid duplicated response.
+ */
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+ P2P_OUI_TYPE);
+ qi->p2p_sd = 1;
+ return;
+}
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MBO
+
+static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype,
+ struct anqp_query_info *qi)
+{
+ switch (subtype) {
+ case MBO_ANQP_SUBTYPE_CELL_CONN_PREF:
+ set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF,
+ "Cellular Data Connection Preference",
+ hapd->conf->mbo_cell_data_conn_pref >= 0, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u",
+ subtype);
+ break;
+ }
+}
+
+
+static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ u8 subtype;
+
+ if (end - pos < 1)
+ return;
+
+ subtype = *pos++;
+ switch (subtype) {
+ case MBO_ANQP_SUBTYPE_QUERY_LIST:
+ wpa_printf(MSG_DEBUG, "ANQP: MBO Query List");
+ while (pos < end) {
+ rx_anqp_mbo_query_list(hapd, *pos, qi);
+ pos++;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u",
+ subtype);
+ break;
+ }
+}
+
+#endif /* CONFIG_MBO */
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ u32 oui;
+
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+ "Query element");
+ return;
+ }
+
+ oui = WPA_GET_BE24(pos);
+ pos += 3;
+ if (oui != OUI_WFA) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+ oui);
+ return;
+ }
+
+ switch (*pos) {
+#ifdef CONFIG_P2P
+ case P2P_OUI_TYPE:
+ rx_anqp_vendor_specific_p2p(hapd, qi);
+ break;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HS20
+ case HS20_ANQP_OUI_TYPE:
+ rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi);
+ break;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ case MBO_ANQP_OUI_TYPE:
+ rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi);
+ break;
+#endif /* CONFIG_MBO */
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+ *pos);
+ break;
+ }
+}
+
+
static void gas_serv_req_local_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
struct anqp_query_info *qi, int prot,
@@ -1189,7 +1470,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
}
#endif /* CONFIG_P2P */
- if (wpabuf_len(buf) > hapd->gas_frag_limit ||
+ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
hapd->conf->gas_comeback_delay) {
struct gas_dialog_info *di;
u16 comeback_delay = 1;
@@ -1240,6 +1521,72 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
}
+#ifdef CONFIG_DPP
+static void gas_serv_req_dpp_processing(struct hostapd_data *hapd,
+ const u8 *sa, u8 dialog_token,
+ int prot, struct wpabuf *buf)
+{
+ struct wpabuf *tx_buf;
+
+ if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
+ hapd->conf->gas_comeback_delay) {
+ struct gas_dialog_info *di;
+ u16 comeback_delay = 1;
+
+ if (hapd->conf->gas_comeback_delay) {
+ /* Testing - allow overriding of the delay value */
+ comeback_delay = hapd->conf->gas_comeback_delay;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too long response to fit in initial response - use GAS comeback");
+ di = gas_dialog_create(hapd, sa, dialog_token);
+ if (!di) {
+ wpa_printf(MSG_INFO, "DPP: Could not create dialog for "
+ MACSTR " (dialog token %u)",
+ MAC2STR(sa), dialog_token);
+ wpabuf_free(buf);
+ tx_buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ 0, 10);
+ if (tx_buf)
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ } else {
+ di->prot = prot;
+ di->sd_resp = buf;
+ di->sd_resp_pos = 0;
+ tx_buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_SUCCESS,
+ comeback_delay, 10);
+ if (tx_buf)
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: GAS Initial response (no comeback)");
+ tx_buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_SUCCESS, 0,
+ 10 + 2 + wpabuf_len(buf));
+ if (tx_buf) {
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_le16(tx_buf, wpabuf_len(buf));
+ wpabuf_put_buf(tx_buf, buf);
+ hostapd_dpp_gas_status_handler(hapd, 1);
+ }
+ wpabuf_free(buf);
+ }
+ if (!tx_buf)
+ return;
+ if (prot)
+ convert_to_protected_dual(tx_buf);
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+}
+#endif /* CONFIG_DPP */
+
+
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
const u8 *sa,
const u8 *data, size_t len, int prot,
@@ -1252,6 +1599,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
u16 slen;
struct anqp_query_info qi;
const u8 *adv_proto;
+#ifdef CONFIG_DPP
+ int dpp = 0;
+#endif /* CONFIG_DPP */
if (len < 1 + 2)
return;
@@ -1279,6 +1629,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
+#ifdef CONFIG_DPP
+ if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC &&
+ pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA &&
+ pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Request");
+ dpp = 1;
+ } else
+#endif /* CONFIG_DPP */
+
if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
struct wpabuf *buf;
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
@@ -1318,6 +1677,18 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
return;
end = pos + slen;
+#ifdef CONFIG_DPP
+ if (dpp) {
+ struct wpabuf *msg;
+
+ msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen);
+ if (!msg)
+ return;
+ gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg);
+ return;
+ }
+#endif /* CONFIG_DPP */
+
/* ANQP Query Request */
while (pos < end) {
u16 info_id, elen;
@@ -1339,11 +1710,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
case ANQP_QUERY_LIST:
rx_anqp_query_list(hapd, pos, pos + elen, &qi);
break;
-#ifdef CONFIG_HS20
case ANQP_VENDOR_SPECIFIC:
rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
break;
-#endif /* CONFIG_HS20 */
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
"Request element %u", info_id);
@@ -1393,8 +1762,8 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
}
frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
- if (frag_len > hapd->gas_frag_limit) {
- frag_len = hapd->gas_frag_limit;
+ if (frag_len > hapd->conf->gas_frag_limit) {
+ frag_len = hapd->conf->gas_frag_limit;
more = 1;
}
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
@@ -1407,6 +1776,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
gas_serv_dialog_clear(dialog);
return;
}
+#ifdef CONFIG_DPP
+ if (dialog->dpp) {
+ tx_buf = gas_build_comeback_resp(dialog_token,
+ WLAN_STATUS_SUCCESS,
+ dialog->sd_frag_id, more, 0,
+ 10 + frag_len);
+ if (tx_buf) {
+ gas_serv_write_dpp_adv_proto(tx_buf);
+ wpabuf_put_buf(tx_buf, buf);
+ }
+ } else
+#endif /* CONFIG_DPP */
tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
WLAN_STATUS_SUCCESS,
dialog->sd_frag_id,
@@ -1430,6 +1811,10 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
} else {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
"SD response sent");
+#ifdef CONFIG_DPP
+ if (dialog->dpp)
+ hostapd_dpp_gas_status_handler(hapd, 1);
+#endif /* CONFIG_DPP */
gas_serv_dialog_clear(dialog);
gas_serv_free_dialogs(hapd, sa);
}
@@ -1495,9 +1880,6 @@ int gas_serv_init(struct hostapd_data *hapd)
{
hapd->public_action_cb2 = gas_serv_rx_public_action;
hapd->public_action_cb2_ctx = hapd;
- hapd->gas_frag_limit = 1400;
- if (hapd->conf->gas_frag_limit > 0)
- hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
return 0;
}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 9051e4f90513..2cf1817298f7 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -41,7 +41,7 @@
#define ANQP_REQ_EMERGENCY_NAI \
(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
/*
- * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
* optimized bitmap.
*/
#define ANQP_REQ_HS_CAPABILITY_LIST \
@@ -60,6 +60,13 @@
(0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
#define ANQP_REQ_ICON_REQUEST \
(0x10000 << HS20_STYPE_ICON_REQUEST)
+#define ANQP_REQ_OPERATOR_ICON_METADATA \
+ (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA)
+#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST)
+/* The first MBO ANQP-element can be included in the optimized bitmap. */
+#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \
+ (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
struct gas_dialog_info {
u8 valid;
@@ -68,6 +75,7 @@ struct gas_dialog_info {
size_t sd_resp_pos; /* Offset in sd_resp */
u8 sd_frag_id;
int prot; /* whether Protected Dual of Public Action frame is used */
+ int dpp; /* whether this is a DPP Config Response */
};
struct hostapd_data;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 9fafc7f457bb..7501bac6e42a 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -31,6 +31,8 @@
#include "vlan_init.h"
#include "wpa_auth.h"
#include "wps_hostapd.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
#include "hw_features.h"
#include "wpa_auth_glue.h"
#include "ap_drv_ops.h"
@@ -45,6 +47,9 @@
#include "ndisc_snoop.h"
#include "neighbor_db.h"
#include "rrm.h"
+#include "fils_hlp.h"
+#include "acs.h"
+#include "hs20.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -52,6 +57,8 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
static int setup_interface2(struct hostapd_iface *iface);
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+ void *timeout_ctx);
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -71,10 +78,26 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
}
+void hostapd_reconfig_encryption(struct hostapd_data *hapd)
+{
+ if (hapd->wpa_auth)
+ return;
+
+ hostapd_set_privacy(hapd, 0);
+ hostapd_setup_encryption(hapd->conf->iface, hapd);
+}
+
+
static void hostapd_reload_bss(struct hostapd_data *hapd)
{
struct hostapd_ssid *ssid;
+ if (!hapd->started)
+ return;
+
+ if (hapd->conf->wmm_enabled < 0)
+ hapd->conf->wmm_enabled = hapd->iconf->ieee80211n;
+
#ifndef CONFIG_NO_RADIUS
radius_client_reconfig(hapd->radius, hapd->conf->radius);
#endif /* CONFIG_NO_RADIUS */
@@ -153,8 +176,27 @@ static void hostapd_clear_old(struct hostapd_iface *iface)
}
+static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ struct hostapd_config *oldconf)
+{
+ size_t i;
+
+ if (newconf->num_bss != oldconf->num_bss)
+ return 1;
+
+ for (i = 0; i < newconf->num_bss; i++) {
+ if (os_strcmp(newconf->bss[i]->iface,
+ oldconf->bss[i]->iface) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
int hostapd_reload_config(struct hostapd_iface *iface)
{
+ struct hapd_interfaces *interfaces = iface->interfaces;
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_config *newconf, *oldconf;
size_t j;
@@ -177,6 +219,35 @@ int hostapd_reload_config(struct hostapd_iface *iface)
hostapd_clear_old(iface);
oldconf = hapd->iconf;
+ if (hostapd_iface_conf_changed(newconf, oldconf)) {
+ char *fname;
+ int res;
+
+ wpa_printf(MSG_DEBUG,
+ "Configuration changes include interface/BSS modification - force full disable+enable sequence");
+ fname = os_strdup(iface->config_fname);
+ if (!fname) {
+ hostapd_config_free(newconf);
+ return -1;
+ }
+ hostapd_remove_iface(interfaces, hapd->conf->iface);
+ iface = hostapd_init(interfaces, fname);
+ os_free(fname);
+ hostapd_config_free(newconf);
+ if (!iface) {
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize interface on config reload");
+ return -1;
+ }
+ iface->interfaces = interfaces;
+ interfaces->iface[interfaces->count] = iface;
+ interfaces->count++;
+ res = hostapd_enable_iface(iface);
+ if (res < 0)
+ wpa_printf(MSG_ERROR,
+ "Failed to enable interface on config reload");
+ return res;
+ }
iface->conf = newconf;
for (j = 0; j < iface->num_bss; j++) {
@@ -210,7 +281,7 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
{
int i;
- if (!ifname)
+ if (!ifname || !hapd->drv_priv)
return;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
@@ -297,6 +368,10 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
#endif /* CONFIG_NO_RADIUS */
hostapd_deinit_wps(hapd);
+#ifdef CONFIG_DPP
+ hostapd_dpp_deinit(hapd);
+ gas_query_ap_deinit(hapd->gas);
+#endif /* CONFIG_DPP */
authsrv_deinit(hapd);
@@ -341,6 +416,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
#endif /* CONFIG_MESH */
hostapd_clean_rrm(hapd);
+ fils_hlp_deinit(hapd);
}
@@ -357,8 +433,10 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
hapd->conf->iface);
if (hapd->iface->interfaces &&
- hapd->iface->interfaces->ctrl_iface_deinit)
+ hapd->iface->interfaces->ctrl_iface_deinit) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
hapd->iface->interfaces->ctrl_iface_deinit(hapd);
+ }
hostapd_free_hapd_data(hapd);
}
@@ -387,8 +465,11 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
hostapd_stop_setup_timers(iface);
#endif /* NEED_AP_MLME */
#endif /* CONFIG_IEEE80211N */
+ if (iface->current_mode)
+ acs_cleanup(iface);
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = NULL;
+ iface->current_mode = NULL;
os_free(iface->current_rates);
iface->current_rates = NULL;
os_free(iface->basic_rates);
@@ -409,6 +490,8 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface,
+ NULL);
hostapd_cleanup_iface_partial(iface);
hostapd_config_free(iface->conf);
@@ -484,9 +567,12 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
ret = -1;
}
}
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
- os_memset(addr, 0xff, ETH_ALEN);
- hostapd_drv_sta_deauth(hapd, addr, reason);
+ if (hapd->conf && hapd->conf->broadcast_deauth) {
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Deauthenticate all stations");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+ }
hostapd_free_stas(hapd);
return ret;
@@ -873,6 +959,48 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
return RADIUS_DAS_SUCCESS;
}
+
+#ifdef CONFIG_HS20
+static enum radius_das_res
+hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ int multi;
+
+ if (hostapd_das_nas_mismatch(hapd, attr))
+ return RADIUS_DAS_NAS_MISMATCH;
+
+ sta = hostapd_das_find_sta(hapd, attr, &multi);
+ if (!sta) {
+ if (multi) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Multiple sessions match - not supported");
+ return RADIUS_DAS_MULTI_SESSION_MATCH;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+ " - CoA", MAC2STR(sta->addr));
+
+ if (attr->hs20_t_c_filtering) {
+ if (attr->hs20_t_c_filtering[0] & BIT(0)) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
+ return RADIUS_DAS_COA_FAILED;
+ }
+
+ hs20_t_c_filtering(hapd, sta, 0);
+ }
+
+ return RADIUS_DAS_SUCCESS;
+}
+#else /* CONFIG_HS20 */
+#define hostapd_das_coa NULL
+#endif /* CONFIG_HS20 */
+
#endif /* CONFIG_NO_RADIUS */
@@ -956,13 +1084,13 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
if (conf->wmm_enabled < 0)
conf->wmm_enabled = hapd->iconf->ieee80211n;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (is_zero_ether_addr(conf->r1_key_holder))
os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_MESH
- if (hapd->iface->mconf == NULL)
+ if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL)
flush_old_stations = 0;
#endif /* CONFIG_MESH */
@@ -1047,6 +1175,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
conf->radius_das_require_message_authenticator;
das_conf.ctx = hapd;
das_conf.disconnect = hostapd_das_disconnect;
+ das_conf.coa = hostapd_das_coa;
hapd->radius_das = radius_das_init(&das_conf);
if (hapd->radius_das == NULL) {
wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
@@ -1063,6 +1192,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
if (hostapd_init_wps(hapd, conf))
return -1;
+#ifdef CONFIG_DPP
+ hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx);
+ if (!hapd->gas)
+ return -1;
+ if (hostapd_dpp_init(hapd))
+ return -1;
+#endif /* CONFIG_DPP */
+
if (authsrv_init(hapd) < 0)
return -1;
@@ -1150,7 +1287,7 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface)
struct hostapd_tx_queue_params *p;
#ifdef CONFIG_MESH
- if (iface->mconf == NULL)
+ if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL)
return;
#endif /* CONFIG_MESH */
@@ -1561,7 +1698,7 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
struct wpa_ssid_value ssid;
u8 channel, op_class;
- int center_freq1 = 0, center_freq2 = 0;
+ u8 center_freq1_idx = 0, center_freq2_idx = 0;
enum nr_chan_width width;
u32 bssid_info;
struct wpabuf *nr;
@@ -1598,22 +1735,22 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
- ieee80211_freq_to_channel_ext(hapd->iface->freq,
- hapd->iconf->secondary_channel,
- hapd->iconf->vht_oper_chwidth,
- &op_class, &channel);
+ 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 = ieee80211_chan_to_freq(
- NULL, op_class,
- hapd->iconf->vht_oper_centr_freq_seg0_idx);
+ center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
if (width == NR_CHAN_WIDTH_80P80)
- center_freq2 = ieee80211_chan_to_freq(
- NULL, op_class,
- hapd->iconf->vht_oper_centr_freq_seg1_idx);
+ center_freq2_idx =
+ hapd->iconf->vht_oper_centr_freq_seg1_idx;
} else if (ht) {
- center_freq1 = hapd->iface->freq +
- 10 * hapd->iconf->secondary_channel;
+ ieee80211_freq_to_chan(hapd->iface->freq +
+ 10 * hapd->iconf->secondary_channel,
+ ¢er_freq1_idx);
}
ssid.ssid_len = hapd->conf->ssid.ssid_len;
@@ -1641,17 +1778,127 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
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);
- wpabuf_put_u8(nr, center_freq2);
+ 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->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)
+{
+ struct hostapd_data *hapd = ctx;
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+
+ if (os_strcmp(hapd->conf->owe_transition_ifname,
+ bss->conf->iface) != 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG,
+ "OWE: ifname=%s found transition mode ifname=%s BSSID "
+ MACSTR " SSID %s",
+ hapd->conf->iface, bss->conf->iface,
+ MAC2STR(bss->own_addr),
+ wpa_ssid_txt(bss->conf->ssid.ssid,
+ bss->conf->ssid.ssid_len));
+ if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len ||
+ is_zero_ether_addr(bss->own_addr))
+ continue;
+
+ os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr,
+ ETH_ALEN);
+ os_memcpy(hapd->conf->owe_transition_ssid,
+ bss->conf->ssid.ssid, bss->conf->ssid.ssid_len);
+ hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len;
+ wpa_printf(MSG_DEBUG,
+ "OWE: Copied transition mode information");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd)
+{
+ if (hapd->conf->owe_transition_ssid_len > 0 &&
+ !is_zero_ether_addr(hapd->conf->owe_transition_bssid))
+ return 0;
+
+ /* Find transition mode SSID/BSSID information from a BSS operated by
+ * this hostapd instance. */
+ if (!hapd->iface->interfaces ||
+ !hapd->iface->interfaces->for_each_interface)
+ return hostapd_owe_iface_iter(hapd->iface, hapd);
+ else
+ return hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_owe_iface_iter, hapd);
+}
+
+
+static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx)
+{
+ size_t i;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+ int res;
+
+ if (!bss->conf->owe_transition_ifname[0])
+ continue;
+ res = hostapd_owe_trans_get_info(bss);
+ if (res == 0)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "OWE: Matching transition mode interface enabled - update beacon data for %s",
+ bss->conf->iface);
+ ieee802_11_set_beacon(bss);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_OWE */
+
+
+static void hostapd_owe_update_trans(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_OWE
+ /* Check whether the enabled BSS can complete OWE transition mode
+ * configuration for any pending interface. */
+ if (!iface->interfaces ||
+ !iface->interfaces->for_each_interface)
+ hostapd_owe_iface_iter2(iface, NULL);
+ else
+ iface->interfaces->for_each_interface(
+ iface->interfaces, hostapd_owe_iface_iter2, NULL);
+#endif /* CONFIG_OWE */
+}
+
+
+static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_iface *iface = eloop_ctx;
+ struct hostapd_data *hapd;
+
+ if (iface->num_bss < 1 || !iface->bss || !iface->bss[0])
+ return;
+ hapd = iface->bss[0];
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+}
+
+
static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
int err)
{
@@ -1827,6 +2074,7 @@ dfs_offload:
#endif /* CONFIG_FST */
hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+ hostapd_owe_update_trans(iface);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
if (hapd->setup_complete_cb)
hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
@@ -1851,8 +2099,19 @@ fail:
iface->fst = NULL;
}
#endif /* CONFIG_FST */
- if (iface->interfaces && iface->interfaces->terminate_on_error)
+
+ if (iface->interfaces && iface->interfaces->terminate_on_error) {
eloop_terminate();
+ } else if (hapd->setup_complete_cb) {
+ /*
+ * Calling hapd->setup_complete_cb directly may cause iface
+ * deinitialization which may be accessed later by the caller.
+ */
+ eloop_register_timeout(0, 0,
+ hostapd_interface_setup_failure_handler,
+ iface, NULL);
+ }
+
return -1;
}
@@ -1997,10 +2256,16 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
hapd->iconf = conf;
hapd->conf = bss;
hapd->iface = hapd_iface;
- hapd->driver = hapd->iconf->driver;
+ if (conf)
+ hapd->driver = conf->driver;
hapd->ctrl_sock = -1;
dl_list_init(&hapd->ctrl_dst);
dl_list_init(&hapd->nr_db);
+ hapd->dhcp_sock = -1;
+#ifdef CONFIG_IEEE80211R_AP
+ dl_list_init(&hapd->l2_queue);
+ dl_list_init(&hapd->l2_oui_queue);
+#endif /* CONFIG_IEEE80211R_AP */
return hapd;
}
@@ -2028,12 +2293,6 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
-#ifdef CONFIG_IEEE80211N
-#ifdef NEED_AP_MLME
- hostapd_stop_setup_timers(iface);
- eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
-#endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211N */
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
iface->wait_channel_update = 0;
@@ -2049,6 +2308,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
break;
hostapd_bss_deinit(iface->bss[j]);
}
+
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
}
@@ -2402,6 +2668,11 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
!!(hapd_iface->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+#ifdef NEED_AP_MLME
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_cleanup_cs_params(hapd_iface->bss[j]);
+#endif /* NEED_AP_MLME */
+
/* same as hostapd_interface_deinit without deinitializing ctrl-iface */
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
@@ -2459,7 +2730,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
if (conf == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
"configuration", __func__);
- return NULL;
+ return NULL;
}
if (driver) {
@@ -2612,6 +2883,7 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
return -1;
}
}
+ hostapd_owe_update_trans(hapd_iface);
return 0;
}
@@ -2829,12 +3101,24 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
ieee802_1x_new_station(hapd, sta);
if (reassoc) {
if (sta->auth_alg != WLAN_AUTH_FT &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK &&
+ sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+ sta->auth_alg != WLAN_AUTH_FILS_PK &&
!(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
} else
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
- if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
+ if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: %s: canceled wired ap_handle_timer timeout for "
+ MACSTR,
+ hapd->conf->iface, __func__,
+ MAC2STR(sta->addr));
+ }
+ } else if (!(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
wpa_printf(MSG_DEBUG,
"%s: %s: reschedule ap_handle_timer timeout for "
MACSTR " (%d seconds - ap_max_inactivity)",
@@ -2928,60 +3212,52 @@ static int hostapd_build_beacon_data(struct hostapd_data *hapd,
goto free_ap_params;
ret = -1;
- beacon->head = os_malloc(params.head_len);
+ beacon->head = os_memdup(params.head, params.head_len);
if (!beacon->head)
goto free_ap_extra_ies;
- os_memcpy(beacon->head, params.head, params.head_len);
beacon->head_len = params.head_len;
- beacon->tail = os_malloc(params.tail_len);
+ beacon->tail = os_memdup(params.tail, params.tail_len);
if (!beacon->tail)
goto free_beacon;
- os_memcpy(beacon->tail, params.tail, params.tail_len);
beacon->tail_len = params.tail_len;
if (params.proberesp != NULL) {
- beacon->probe_resp = os_malloc(params.proberesp_len);
+ beacon->probe_resp = os_memdup(params.proberesp,
+ params.proberesp_len);
if (!beacon->probe_resp)
goto free_beacon;
- os_memcpy(beacon->probe_resp, params.proberesp,
- params.proberesp_len);
beacon->probe_resp_len = params.proberesp_len;
}
/* copy the extra ies */
if (beacon_extra) {
- beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
+ beacon->beacon_ies = os_memdup(beacon_extra->buf,
+ wpabuf_len(beacon_extra));
if (!beacon->beacon_ies)
goto free_beacon;
- os_memcpy(beacon->beacon_ies,
- beacon_extra->buf, wpabuf_len(beacon_extra));
beacon->beacon_ies_len = wpabuf_len(beacon_extra);
}
if (proberesp_extra) {
- beacon->proberesp_ies =
- os_malloc(wpabuf_len(proberesp_extra));
+ beacon->proberesp_ies = os_memdup(proberesp_extra->buf,
+ wpabuf_len(proberesp_extra));
if (!beacon->proberesp_ies)
goto free_beacon;
- os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
- wpabuf_len(proberesp_extra));
beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
}
if (assocresp_extra) {
- beacon->assocresp_ies =
- os_malloc(wpabuf_len(assocresp_extra));
+ beacon->assocresp_ies = os_memdup(assocresp_extra->buf,
+ wpabuf_len(assocresp_extra));
if (!beacon->assocresp_ies)
goto free_beacon;
- os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
- wpabuf_len(assocresp_extra));
beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
}
@@ -3158,6 +3434,19 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
}
+void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled)
+{
+ if (vht_enabled)
+ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
+ else
+ hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
+
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x",
+ hapd->iconf->ch_switch_vht_config);
+}
+
+
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings)
{
@@ -3192,7 +3481,6 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params)
{
int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
- unsigned int i;
wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
@@ -3234,10 +3522,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
/*
* cs_params must not be cleared earlier because the freq_params
* argument may actually point to one of these.
+ * These params will be cleared during interface disable below.
*/
- for (i = 0; i < iface->num_bss; i++)
- hostapd_cleanup_cs_params(iface->bss[i]);
-
hostapd_disable_iface(iface);
hostapd_enable_iface(iface);
}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dec46f692206..d304c1171810 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -14,6 +14,13 @@
#include "ap_config.h"
#include "drivers/driver.h"
+#define OCE_STA_CFON_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_STA_CFON) && \
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
+#define OCE_AP_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_AP) && \
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
+
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
@@ -53,7 +60,16 @@ struct hapd_interfaces {
#ifndef CONFIG_NO_VLAN
struct dynamic_iface *vlan_priv;
#endif /* CONFIG_NO_VLAN */
+#ifdef CONFIG_ETH_P_OUI
+ struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
+#endif /* CONFIG_ETH_P_OUI */
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 */
+#endif /* CONFIG_DPP */
};
enum hostapd_chan_status {
@@ -76,6 +92,7 @@ struct hostapd_rate_data {
};
struct hostapd_frame_info {
+ unsigned int freq;
u32 channel;
u32 datarate;
int ssi_signal; /* dBm */
@@ -109,6 +126,7 @@ struct hostapd_neighbor_entry {
struct wpabuf *civic;
/* LCI update time */
struct os_time lci_date;
+ int stationary;
};
/**
@@ -184,6 +202,17 @@ struct hostapd_data {
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
struct l2_packet_data *l2;
+
+#ifdef CONFIG_IEEE80211R_AP
+ struct dl_list l2_queue;
+ struct dl_list l2_oui_queue;
+ struct eth_p_oui_ctx *oui_pull;
+ struct eth_p_oui_ctx *oui_resp;
+ struct eth_p_oui_ctx *oui_push;
+ struct eth_p_oui_ctx *oui_sreq;
+ struct eth_p_oui_ctx *oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+
struct wps_context *wps;
int beacon_set_done;
@@ -242,9 +271,6 @@ struct hostapd_data {
unsigned int cs_c_off_ecsa_beacon;
unsigned int cs_c_off_ecsa_proberesp;
- /* BSS Load */
- unsigned int bss_load_update_timeout;
-
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -259,9 +285,6 @@ struct hostapd_data {
int noa_start;
int noa_duration;
#endif /* CONFIG_P2P */
-#ifdef CONFIG_INTERWORKING
- size_t gas_frag_limit;
-#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_PROXYARP
struct l2_packet_data *sock_dhcp;
struct l2_packet_data *sock_ndisc;
@@ -292,6 +315,18 @@ struct hostapd_data {
unsigned int ext_eapol_frame_io:1;
struct l2_packet_data *l2_test;
+
+ enum wpa_alg last_gtk_alg;
+ int last_gtk_key_idx;
+ u8 last_gtk[WPA_GTK_MAX_LEN];
+ size_t last_gtk_len;
+
+#ifdef CONFIG_IEEE80211W
+ enum wpa_alg last_igtk_alg;
+ int last_igtk_key_idx;
+ u8 last_igtk[WPA_IGTK_MAX_LEN];
+ size_t last_igtk_len;
+#endif /* CONFIG_IEEE80211W */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
@@ -300,10 +335,42 @@ struct hostapd_data {
struct dl_list nr_db;
+ u8 beacon_req_token;
u8 lci_req_token;
u8 range_req_token;
unsigned int lci_req_active:1;
unsigned int range_req_active:1;
+
+ int dhcp_sock; /* UDP socket used with the DHCP server */
+
+#ifdef CONFIG_DPP
+ int dpp_init_done;
+ struct dpp_authentication *dpp_auth;
+ u8 dpp_allowed_roles;
+ int dpp_qr_mutual;
+ int dpp_auth_ok_on_ack;
+ int dpp_in_response_listen;
+ struct gas_query_ap *gas;
+ struct dpp_pkex *dpp_pkex;
+ struct dpp_bootstrap_info *dpp_pkex_bi;
+ char *dpp_pkex_code;
+ char *dpp_pkex_identifier;
+ char *dpp_pkex_auth_cmd;
+ char *dpp_configurator_params;
+ struct os_reltime dpp_last_init;
+ struct os_reltime dpp_init_iter_start;
+ unsigned int dpp_init_max_tries;
+ unsigned int dpp_init_retry_time;
+ unsigned int dpp_resp_wait_time;
+ unsigned int dpp_resp_max_tries;
+ unsigned int dpp_resp_retry_time;
+#ifdef CONFIG_TESTING_OPTIONS
+ char *dpp_config_obj_override;
+ char *dpp_discovery_override;
+ char *dpp_groups_override;
+ unsigned int dpp_ignore_netaccesskey_mismatch:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_DPP */
};
@@ -311,6 +378,7 @@ struct hostapd_sta_info {
struct dl_list list;
u8 addr[ETH_ALEN];
struct os_reltime last_seen;
+ int ssi_signal;
#ifdef CONFIG_TAXONOMY
struct wpabuf *probe_ie_taxonomy;
#endif /* CONFIG_TAXONOMY */
@@ -440,6 +508,10 @@ struct hostapd_iface {
u64 last_channel_time_busy;
u8 channel_utilization;
+ unsigned int chan_util_samples_sum;
+ unsigned int chan_util_num_sample_periods;
+ unsigned int chan_util_average;
+
/* eCSA IE will be added only if operating class is specified */
u8 cs_oper_class;
@@ -459,6 +531,8 @@ struct hostapd_iface {
struct dl_list sta_seen; /* struct hostapd_sta_info */
unsigned int num_sta_seen;
+
+ u8 dfs_domain;
};
/* hostapd.c */
@@ -466,6 +540,7 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
int (*cb)(struct hostapd_iface *iface,
void *ctx), void *ctx);
int hostapd_reload_config(struct hostapd_iface *iface);
+void hostapd_reconfig_encryption(struct hostapd_data *hapd);
struct hostapd_data *
hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
struct hostapd_config *conf,
@@ -492,6 +567,7 @@ void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
const char * hostapd_state_text(enum hostapd_iface_state s);
int hostapd_csa_in_progress(struct hostapd_iface *iface);
+void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
void
@@ -499,6 +575,7 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params);
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
void hostapd_periodic_iface(struct hostapd_iface *iface);
+int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -510,6 +587,8 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd,
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
/* drv_callbacks.c (TODO: move to somewhere else?) */
+void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta);
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ielen, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
@@ -533,6 +612,9 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
const char *ifname);
+void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
+ enum smps_mode smps_mode,
+ enum chan_width chan_width, u8 rx_nss);
#ifdef CONFIG_FST
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index d7909fad4a14..e265569aef38 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -11,9 +11,11 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "sta_info.h"
#include "hs20.h"
@@ -175,3 +177,72 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
return ret;
}
+
+
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+ const u8 *addr, const char *url)
+{
+ struct wpabuf *buf;
+ int ret;
+ size_t url_len;
+
+ if (!url) {
+ wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
+ return -1;
+ }
+
+ url_len = os_strlen(url);
+ if (5 + url_len > 255) {
+ wpa_printf(MSG_INFO,
+ "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
+ url);
+ return -1;
+ }
+
+ buf = wpabuf_alloc(4 + 7 + url_len);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Terms and Conditions Acceptance subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + 1 + url_len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
+ wpabuf_put_u8(buf, url_len);
+ wpabuf_put_str(buf, url);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+ int enabled)
+{
+ if (enabled) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering required for "
+ MACSTR, MAC2STR(sta->addr));
+ sta->hs20_t_c_filtering = 1;
+ /* TODO: Enable firewall filtering for the STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
+ MAC2STR(sta->addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering not required for "
+ MACSTR, MAC2STR(sta->addr));
+ sta->hs20_t_c_filtering = 0;
+ /* TODO: Disable firewall filtering for the STA */
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
+ }
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
index 152439f4dcb4..e99e26e91158 100644
--- a/src/ap/hs20.h
+++ b/src/ap/hs20.h
@@ -18,5 +18,9 @@ int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
const u8 *addr,
const struct wpabuf *payload);
+int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
+ const u8 *addr, const char *url);
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+ int enabled);
#endif /* HS20_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 16887acdfef4..5279abca1f1f 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -78,10 +78,12 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
int i, j;
u16 num_modes, flags;
struct hostapd_hw_modes *modes;
+ u8 dfs_domain;
if (hostapd_drv_none(hapd))
return -1;
- modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
+ modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
+ &dfs_domain);
if (modes == NULL) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -91,6 +93,7 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
}
iface->hw_flags = flags;
+ iface->dfs_domain = dfs_domain;
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = modes;
@@ -329,6 +332,9 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
res = ieee80211n_allowed_ht40_channel_pair(iface);
if (!res) {
iface->conf->secondary_channel = 0;
+ iface->conf->vht_oper_centr_freq_seg0_idx = 0;
+ iface->conf->vht_oper_centr_freq_seg1_idx = 0;
+ iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
res = 1;
wpa_printf(MSG_INFO, "Fallback to 20 MHz");
}
@@ -621,41 +627,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
#ifdef CONFIG_IEEE80211AC
-
-static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
-{
- u32 req_cap = conf & cap;
-
- /*
- * Make sure we support all requested capabilities.
- * NOTE: We assume that 'cap' represents a capability mask,
- * not a discrete value.
- */
- if ((hw & req_cap) != req_cap) {
- wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
- name);
- return 0;
- }
- return 1;
-}
-
-
-static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
- unsigned int shift,
- const char *name)
-{
- u32 hw_max = hw & mask;
- u32 conf_val = conf & mask;
-
- if (conf_val > hw_max) {
- wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
- name, conf_val >> shift, hw_max >> shift);
- return 0;
- }
- return 1;
-}
-
-
static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
{
struct hostapd_hw_modes *mode = iface->current_mode;
@@ -683,45 +654,7 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
}
}
-#define VHT_CAP_CHECK(cap) \
- do { \
- if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
- return 0; \
- } while (0)
-
-#define VHT_CAP_CHECK_MAX(cap) \
- do { \
- if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
- #cap)) \
- return 0; \
- } while (0)
-
- VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
- VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
- VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
- VHT_CAP_CHECK(VHT_CAP_RXLDPC);
- VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
- VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
- VHT_CAP_CHECK(VHT_CAP_TXSTBC);
- VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
- VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
- VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
- VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
- VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
- VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
- VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
- VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
- VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
- VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
- VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
- VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
- VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
-
-#undef VHT_CAP_CHECK
-#undef VHT_CAP_CHECK_MAX
-
- return 1;
+ return ieee80211ac_cap_check(hw, conf);
}
#endif /* CONFIG_IEEE80211AC */
@@ -746,7 +679,8 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
if (!ieee80211n_supported_ht_capab(iface))
return -1;
#ifdef CONFIG_IEEE80211AC
- if (!ieee80211ac_supported_vht_capab(iface))
+ if (iface->conf->ieee80211ac &&
+ !ieee80211ac_supported_vht_capab(iface))
return -1;
#endif /* CONFIG_IEEE80211AC */
ret = ieee80211n_check_40mhz(iface);
@@ -785,20 +719,41 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface,
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
}
+ wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
+ channel, primary ? "primary" : "secondary");
return 0;
}
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
+ int secondary_chan;
+
if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
return 0;
if (!iface->conf->secondary_channel)
return 1;
- return hostapd_is_usable_chan(iface, iface->conf->channel +
- iface->conf->secondary_channel * 4, 0);
+ if (!iface->conf->ht40_plus_minus_allowed)
+ return hostapd_is_usable_chan(
+ iface, iface->conf->channel +
+ iface->conf->secondary_channel * 4, 0);
+
+ /* 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)) {
+ iface->conf->secondary_channel = 1;
+ return 1;
+ }
+
+ secondary_chan = iface->conf->channel - 4;
+ if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+ iface->conf->secondary_channel = -1;
+ return 1;
+ }
+
+ return 0;
}
@@ -978,5 +933,19 @@ int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
{
- return hw_get_chan(hapd->iface->current_mode, freq);
+ int i, channel;
+ struct hostapd_hw_modes *mode;
+
+ channel = hw_get_chan(hapd->iface->current_mode, freq);
+ if (channel)
+ return channel;
+ /* Check other available modes since the channel list for the current
+ * mode did not include the specified frequency. */
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+ channel = hw_get_chan(mode, freq);
+ if (channel)
+ return channel;
+ }
+ return 0;
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 333035fe7703..f9bb99d98549 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2014, Jouni Malinen
+ * Copyright (c) 2002-2017, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,6 +14,8 @@
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -45,8 +47,21 @@
#include "mbo_ap.h"
#include "rrm.h"
#include "taxonomy.h"
+#include "fils_hlp.h"
+#include "dpp_hostapd.h"
+#include "gas_query_ap.h"
+#ifdef CONFIG_FILS
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 *resp,
+ struct rsn_pmksa_cache_entry *pmksa,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len,
+ int *is_pub);
+#endif /* CONFIG_FILS */
+
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
@@ -262,7 +277,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
static int send_auth_reply(struct hostapd_data *hapd,
const u8 *dst, const u8 *bssid,
u16 auth_alg, u16 auth_transaction, u16 resp,
- const u8 *ies, size_t ies_len)
+ const u8 *ies, size_t ies_len, const char *dbg)
{
struct ieee80211_mgmt *reply;
u8 *buf;
@@ -289,9 +304,9 @@ static int send_auth_reply(struct hostapd_data *hapd,
os_memcpy(reply->u.auth.variable, ies, ies_len);
wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
- " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
+ " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
MAC2STR(dst), auth_alg, auth_transaction,
- resp, (unsigned long) ies_len);
+ resp, (unsigned long) ies_len, dbg);
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
wpa_printf(MSG_INFO, "send_auth_reply: send failed");
else
@@ -303,7 +318,7 @@ static int send_auth_reply(struct hostapd_data *hapd,
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len)
@@ -313,7 +328,8 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
int reply_res;
reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT,
- auth_transaction, status, ies, ies_len);
+ auth_transaction, status, ies, ies_len,
+ "auth-ft-finish");
sta = ap_get_sta(hapd, dst);
if (sta == NULL)
@@ -334,38 +350,65 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
sta->flags |= WLAN_STA_AUTH;
mlme_authenticate_indication(hapd, sta);
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
-#define dot11RSNASAESync 5 /* attempts */
+static void sae_set_state(struct sta_info *sta, enum sae_state state,
+ const char *reason)
+{
+ wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
+ sae_state_txt(sta->sae->state), sae_state_txt(state),
+ MAC2STR(sta->addr), reason);
+ sta->sae->state = state;
+}
static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta, int update)
{
struct wpabuf *buf;
+ const char *password = NULL;
+ struct sae_password_entry *pw;
+ const char *rx_id = NULL;
- if (hapd->conf->ssid.wpa_passphrase == NULL) {
+ if (sta->sae->tmp)
+ rx_id = sta->sae->tmp->pw_id;
+
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
+ continue;
+ if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
+ continue;
+ if (rx_id && pw->identifier &&
+ os_strcmp(rx_id, pw->identifier) != 0)
+ continue;
+ password = pw->password;
+ break;
+ }
+ if (!password)
+ password = hapd->conf->ssid.wpa_passphrase;
+ if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
if (update &&
sae_prepare_commit(hapd->own_addr, sta->addr,
- (u8 *) hapd->conf->ssid.wpa_passphrase,
- os_strlen(hapd->conf->ssid.wpa_passphrase),
+ (u8 *) password, os_strlen(password), rx_id,
sta->sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
- buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
+ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
+ (rx_id ? 3 + os_strlen(rx_id) : 0));
if (buf == NULL)
return NULL;
sae_write_commit(sta->sae, buf, sta->sae->tmp ?
- sta->sae->tmp->anti_clogging_token : NULL);
+ sta->sae->tmp->anti_clogging_token : NULL, rx_id);
return buf;
}
@@ -394,12 +437,14 @@ static int auth_sae_send_commit(struct hostapd_data *hapd,
int reply_res;
data = auth_build_sae_commit(hapd, sta, update);
+ if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
+ return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
- wpabuf_len(data));
+ wpabuf_len(data), "sae-send-commit");
wpabuf_free(data);
@@ -420,7 +465,7 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd,
reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
- wpabuf_len(data));
+ wpabuf_len(data), "sae-send-confirm");
wpabuf_free(data);
@@ -499,10 +544,10 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
}
-static int sae_check_big_sync(struct sta_info *sta)
+static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
{
- if (sta->sae->sync > dot11RSNASAESync) {
- sta->sae->state = SAE_NOTHING;
+ if (sta->sae->sync > hapd->conf->sae_sync) {
+ sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
return -1;
}
@@ -516,12 +561,13 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
struct sta_info *sta = eloop_data;
int ret;
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return;
sta->sae->sync++;
wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
- " (sync=%d state=%d)",
- MAC2STR(sta->addr), sta->sae->sync, sta->sae->state);
+ " (sync=%d state=%s)",
+ MAC2STR(sta->addr), sta->sae->sync,
+ sae_state_txt(sta->sae->state));
switch (sta->sae->state) {
case SAE_COMMITTED:
@@ -570,7 +616,7 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
sta->auth_alg = WLAN_AUTH_SAE;
mlme_authenticate_indication(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
- sta->sae->state = SAE_ACCEPTED;
+ sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmkid);
}
@@ -584,13 +630,16 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
if (auth_transaction != 1 && auth_transaction != 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
+ MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
+ auth_transaction);
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
ret = auth_sae_send_commit(hapd, sta, bssid, 1);
if (ret)
return ret;
- sta->sae->state = SAE_COMMITTED;
+ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -612,7 +661,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
- sta->sae->state = SAE_CONFIRMED;
+ sae_set_state(sta, SAE_CONFIRMED,
+ "Sent Confirm (mesh)");
} else {
/*
* For infrastructure BSS, send only the Commit
@@ -641,7 +691,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
- sta->sae->state = SAE_CONFIRMED;
+ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
} else if (hapd->conf->mesh & MESH_ENABLED) {
@@ -649,7 +699,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
* In mesh case, follow SAE finite state machine and
* send Commit now, if sync count allows.
*/
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -668,7 +718,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
if (ret)
return ret;
- sta->sae->state = SAE_CONFIRMED;
+ sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
/*
* Since this was triggered on Confirm RX, run another
@@ -681,7 +731,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
case SAE_CONFIRMED:
sae_clear_retransmit_timer(hapd, sta);
if (auth_transaction == 1) {
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -698,18 +748,31 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
sae_set_retransmit_timer(hapd, sta);
} else {
+ sta->sae->send_confirm = 0xffff;
sae_accept_sta(hapd, sta);
}
break;
case SAE_ACCEPTED:
- if (auth_transaction == 1) {
+ if (auth_transaction == 1 &&
+ (hapd->conf->mesh & MESH_ENABLED)) {
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);
+ } else if (auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+ sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
} else {
- if (sae_check_big_sync(sta))
+ if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
@@ -773,6 +836,29 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
+#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;
+ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp, pos, end - pos,
+ "auth-sae-reflection-attack");
+ goto remove_sta;
+ }
+
+ if (hapd->conf->sae_commit_override && auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
+ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp,
+ wpabuf_head(hapd->conf->sae_commit_override),
+ wpabuf_len(hapd->conf->sae_commit_override),
+ "sae-commit-override");
+ goto remove_sta;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
if (!sta->sae) {
if (auth_transaction != 1 ||
status_code != WLAN_STATUS_SUCCESS) {
@@ -784,7 +870,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
resp = -1;
goto remove_sta;
}
- sta->sae->state = SAE_NOTHING;
+ sae_set_state(sta, SAE_NOTHING, "Init");
sta->sae->sync = 0;
}
@@ -847,7 +933,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
"SAE: Failed to send commit message");
goto remove_sta;
}
- sta->sae->state = SAE_COMMITTED;
+ sae_set_state(sta, SAE_COMMITTED,
+ "Sent Commit (anti-clogging token case in mesh)");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
return;
@@ -866,6 +953,20 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
if (status_code != WLAN_STATUS_SUCCESS)
goto remove_sta;
+ if (!(hapd->conf->mesh & MESH_ENABLED) &&
+ sta->sae->state == SAE_COMMITTED) {
+ /* This is needed in the infrastructure BSS case to
+ * address a sequence where a STA entry may remain in
+ * hostapd across two attempts to do SAE authentication
+ * by the same STA. The second attempt may end up trying
+ * 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);
+ }
+
resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
((const u8 *) mgmt) + len -
mgmt->u.auth.variable, &token,
@@ -876,6 +977,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
MAC2STR(sta->addr));
goto remove_sta;
}
+
+ if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
+ MACSTR, MAC2STR(sta->addr));
+ sae_clear_retransmit_timer(hapd, sta);
+ sae_set_state(sta, SAE_NOTHING,
+ "Unknown Password Identifier");
+ goto remove_sta;
+ }
+
if (token && check_sae_token(hapd, sta->addr, token, token_len)
< 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
@@ -896,7 +1008,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
sta->addr);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
if (hapd->conf->mesh & MESH_ENABLED)
- sta->sae->state = SAE_NOTHING;
+ sae_set_state(sta, SAE_NOTHING,
+ "Request anti-clogging token case in mesh");
goto reply;
}
@@ -910,12 +1023,36 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
goto remove_sta;
if (sta->sae->state >= SAE_CONFIRMED ||
!(hapd->conf->mesh & MESH_ENABLED)) {
- if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
- ((u8 *) mgmt) + len -
- mgmt->u.auth.variable) < 0) {
+ const u8 *var;
+ size_t var_len;
+ u16 peer_send_confirm;
+
+ var = mgmt->u.auth.variable;
+ var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
+ if (var_len < 2) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
+
+ peer_send_confirm = WPA_GET_LE16(var);
+
+ if (sta->sae->state == SAE_ACCEPTED &&
+ (peer_send_confirm <= sta->sae->rc ||
+ peer_send_confirm == 0xffff)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Silently ignore unexpected Confirm from peer "
+ MACSTR
+ " (peer-send-confirm=%u Rc=%u)",
+ MAC2STR(sta->addr),
+ peer_send_confirm, sta->sae->rc);
+ return;
+ }
+
+ if (sae_check_confirm(sta->sae, var, var_len) < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ sta->sae->rc = peer_send_confirm;
}
resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
} else {
@@ -933,7 +1070,7 @@ reply:
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
- data ? wpabuf_len(data) : 0);
+ data ? wpabuf_len(data) : 0, "auth-sae");
}
remove_sta:
@@ -970,7 +1107,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
if (ret)
return -1;
- sta->sae->state = SAE_COMMITTED;
+ sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
@@ -980,6 +1117,631 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
#endif /* CONFIG_SAE */
+static u16 wpa_res_to_status_code(int res)
+{
+ if (res == WPA_INVALID_GROUP)
+ return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ if (res == WPA_INVALID_PAIRWISE)
+ return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ if (res == WPA_INVALID_AKMP)
+ return WLAN_STATUS_AKMP_NOT_VALID;
+ if (res == WPA_ALLOC_FAIL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_IEEE80211W
+ if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+ return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+#endif /* CONFIG_IEEE80211W */
+ if (res == WPA_INVALID_MDIE)
+ return WLAN_STATUS_INVALID_MDIE;
+ if (res == WPA_INVALID_PMKID)
+ return WLAN_STATUS_INVALID_PMKID;
+ if (res != WPA_IE_OK)
+ return WLAN_STATUS_INVALID_IE;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+#ifdef CONFIG_FILS
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub);
+
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, size_t len, u16 auth_alg,
+ u16 auth_transaction, u16 status_code,
+ void (*cb)(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub))
+{
+ u16 resp = WLAN_STATUS_SUCCESS;
+ const u8 *end;
+ struct ieee802_11_elems elems;
+ int res;
+ struct wpa_ie_data rsn;
+ struct rsn_pmksa_cache_entry *pmksa = NULL;
+
+ if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+ return;
+
+ end = pos + len;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+ pos, end - pos);
+
+ /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+ if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
+ u16 group;
+ struct wpabuf *pub;
+ size_t elem_len;
+
+ /* Using FILS PFS */
+
+ /* Finite Cyclic Group */
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No room for Finite Cyclic Group");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ group = WPA_GET_LE16(pos);
+ pos += 2;
+ if (group != hapd->conf->fils_dh_group) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
+ group, hapd->conf->fils_dh_group);
+ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ sta->fils_ecdh = crypto_ecdh_init(group);
+ if (!sta->fils_ecdh) {
+ wpa_printf(MSG_INFO,
+ "FILS: Could not initialize ECDH with group %d",
+ group);
+ resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+ if (!pub) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to derive ECDH public key");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ elem_len = wpabuf_len(pub);
+ wpabuf_free(pub);
+
+ /* Element */
+ if ((size_t) (end - pos) < elem_len) {
+ wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ wpabuf_free(sta->fils_g_sta);
+ sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
+ pos, elem_len);
+ if (!sta->fils_dh_ss) {
+ wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
+ pos += elem_len;
+ } else {
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ sta->fils_ecdh = NULL;
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = NULL;
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+ if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ /* RSNE */
+ wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
+ elems.rsn_ie, elems.rsn_ie_len);
+ if (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (!sta->wpa_sm)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
+ NULL);
+ if (!sta->wpa_sm) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to initialize RSN state machine");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ elems.mdie, elems.mdie_len, NULL, 0);
+ resp = wpa_res_to_status_code(res);
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ if (!elems.fils_nonce) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
+ FILS_NONCE_LEN);
+ os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+ /* PMKID List */
+ if (rsn.pmkid && rsn.num_pmkid > 0) {
+ u8 num;
+ const u8 *pmkid;
+
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+ rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+ pmkid = rsn.pmkid;
+ num = rsn.num_pmkid;
+ while (num) {
+ wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
+ pmkid);
+ if (pmksa)
+ break;
+ pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
+ sta->addr,
+ pmkid);
+ if (pmksa)
+ break;
+ pmkid += PMKID_LEN;
+ num--;
+ }
+ }
+ if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
+ wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
+ pmksa = NULL;
+ }
+ if (pmksa)
+ wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
+
+ /* FILS Session */
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+ os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
+
+ /* FILS Wrapped Data */
+ if (elems.fils_wrapped_data) {
+ wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+ elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len);
+ if (!pmksa) {
+#ifndef CONFIG_NO_RADIUS
+ if (!sta->eapol_sm) {
+ sta->eapol_sm =
+ ieee802_1x_alloc_eapol_sm(hapd, sta);
+ }
+ wpa_printf(MSG_DEBUG,
+ "FILS: Forward EAP-Initiate/Re-auth to authentication server");
+ ieee802_1x_encapsulate_radius(
+ hapd, sta, elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len);
+ sta->fils_pending_cb = cb;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Will send Authentication frame once the response from authentication server is available");
+ sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+ /* Calculate pending PMKID here so that we do not need
+ * to maintain a copy of the EAP-Initiate/Reauth
+ * message. */
+ if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ elems.fils_wrapped_data,
+ elems.fils_wrapped_data_len,
+ sta->fils_erp_pmkid) == 0)
+ sta->fils_erp_pmkid_set = 1;
+ return;
+#else /* CONFIG_NO_RADIUS */
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+#endif /* CONFIG_NO_RADIUS */
+ }
+ }
+
+fail:
+ if (cb) {
+ struct wpabuf *data;
+ int pub = 0;
+
+ data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
+ NULL, 0, &pub);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "%s: prepare_auth_resp_fils() returned failure",
+ __func__);
+ }
+
+ cb(hapd, sta, resp, data, pub);
+ }
+}
+
+
+static struct wpabuf *
+prepare_auth_resp_fils(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 *resp,
+ struct rsn_pmksa_cache_entry *pmksa,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len,
+ int *is_pub)
+{
+ u8 fils_nonce[FILS_NONCE_LEN];
+ size_t ielen;
+ struct wpabuf *data = NULL;
+ const u8 *ie;
+ u8 *ie_buf = NULL;
+ const u8 *pmk = NULL;
+ size_t pmk_len = 0;
+ u8 pmk_buf[PMK_LEN_MAX];
+ struct wpabuf *pub = NULL;
+
+ if (*resp != WLAN_STATUS_SUCCESS)
+ goto fail;
+
+ ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+ if (!ie) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ if (pmksa) {
+ /* Add PMKID of the selected PMKSA into RSNE */
+ ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
+ if (!ie_buf) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ os_memcpy(ie_buf, ie, ielen);
+ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ ie = ie_buf;
+ }
+
+ if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
+ fils_nonce, FILS_NONCE_LEN);
+
+#ifdef CONFIG_FILS_SK_PFS
+ if (sta->fils_dh_ss && sta->fils_ecdh) {
+ pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+ if (!pub) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
+ if (!data) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+ if (pub) {
+ /* Finite Cyclic Group */
+ wpabuf_put_le16(data, hapd->conf->fils_dh_group);
+
+ /* Element */
+ wpabuf_put_buf(data, pub);
+ }
+#endif /* CONFIG_FILS_SK_PFS */
+
+ /* RSNE */
+ wpabuf_put_data(data, ie, ielen);
+
+ /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
+ /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
+ int res;
+ int use_sha384 = wpa_key_mgmt_sha384(
+ wpa_auth_sta_key_mgmt(sta->wpa_sm));
+
+ res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
+ wpabuf_put(data, 0),
+ wpabuf_tailroom(data));
+ if (res < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpabuf_put(data, res);
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ /* FILS Nonce */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
+
+ /* FILS Wrapped Data */
+ if (!pmksa && erp_resp) {
+ wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+ wpabuf_put_buf(data, erp_resp);
+
+ if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ msk, msk_len, sta->fils_snonce, fils_nonce,
+ sta->fils_dh_ss ?
+ wpabuf_head(sta->fils_dh_ss) : NULL,
+ sta->fils_dh_ss ?
+ wpabuf_len(sta->fils_dh_ss) : 0,
+ pmk_buf, &pmk_len)) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(data);
+ data = NULL;
+ goto fail;
+ }
+ pmk = pmk_buf;
+
+ /* Don't use DHss in PTK derivation if PMKSA caching is not
+ * used. */
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = NULL;
+
+ if (sta->fils_erp_pmkid_set) {
+ /* TODO: get PMKLifetime from WPA parameters */
+ unsigned int dot11RSNAConfigPMKLifetime = 43200;
+ int session_timeout;
+
+ session_timeout = dot11RSNAConfigPMKLifetime;
+ if (sta->session_timeout_set) {
+ struct os_reltime now, diff;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now,
+ &diff);
+ session_timeout = diff.sec;
+ }
+
+ sta->fils_erp_pmkid_set = 0;
+ if (wpa_auth_pmksa_add2(
+ hapd->wpa_auth, sta->addr,
+ pmk, pmk_len,
+ sta->fils_erp_pmkid,
+ session_timeout,
+ wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to add PMKSA cache entry based on ERP");
+ }
+ }
+ } else if (pmksa) {
+ pmk = pmksa->pmk;
+ pmk_len = pmksa->pmk_len;
+ }
+
+ if (!pmk) {
+ wpa_printf(MSG_DEBUG, "FILS: No PMK available");
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(data);
+ data = NULL;
+ goto fail;
+ }
+
+ if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
+ sta->fils_snonce, fils_nonce,
+ sta->fils_dh_ss ?
+ wpabuf_head(sta->fils_dh_ss) : NULL,
+ sta->fils_dh_ss ?
+ wpabuf_len(sta->fils_dh_ss) : 0,
+ sta->fils_g_sta, pub) < 0) {
+ *resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(data);
+ data = NULL;
+ goto fail;
+ }
+
+fail:
+ if (is_pub)
+ *is_pub = pub != NULL;
+ os_free(ie_buf);
+ wpabuf_free(pub);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ sta->fils_dh_ss = NULL;
+#ifdef CONFIG_FILS_SK_PFS
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ sta->fils_ecdh = NULL;
+#endif /* CONFIG_FILS_SK_PFS */
+ return data;
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 resp,
+ struct wpabuf *data, int pub)
+{
+ u16 auth_alg;
+
+ auth_alg = (pub ||
+ resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
+ WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+ send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
+ data ? wpabuf_head(data) : (u8 *) "",
+ data ? wpabuf_len(data) : 0, "auth-fils-finish");
+ wpabuf_free(data);
+
+ if (resp == WLAN_STATUS_SUCCESS) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "authentication OK (FILS)");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+ mlme_authenticate_indication(hapd, sta);
+ }
+}
+
+
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len)
+{
+ struct wpabuf *data;
+ int pub = 0;
+ u16 resp;
+
+ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+
+ if (!sta->fils_pending_cb)
+ return;
+ resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
+ data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
+ msk, msk_len, &pub);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "%s: prepare_auth_resp_fils() returned failure",
+ __func__);
+ }
+ sta->fils_pending_cb(hapd, sta, resp, data, pub);
+}
+
+#endif /* CONFIG_FILS */
+
+
+int
+ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval,
+ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui, int is_probe_req)
+{
+ int res;
+
+ os_memset(vlan_id, 0, sizeof(*vlan_id));
+ res = hostapd_allowed_address(hapd, addr, msg, len,
+ session_timeout, acct_interim_interval,
+ vlan_id, psk, identity, radius_cui,
+ is_probe_req);
+
+ if (res == HOSTAPD_ACL_REJECT) {
+ if (!is_probe_req)
+ wpa_printf(MSG_DEBUG,
+ "Station " MACSTR
+ " not allowed to authenticate",
+ MAC2STR(addr));
+ return HOSTAPD_ACL_REJECT;
+ }
+
+ if (res == HOSTAPD_ACL_PENDING) {
+ wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+ " waiting for an external authentication",
+ MAC2STR(addr));
+ /* Authentication code will re-send the authentication frame
+ * after it has received (and cached) information from the
+ * external source. */
+ return HOSTAPD_ACL_PENDING;
+ }
+
+ return res;
+}
+
+
+static int
+ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, u32 session_timeout,
+ u32 acct_interim_interval,
+ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui)
+{
+ if (vlan_id->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ vlan_id->untagged,
+ vlan_id->tagged[0] ? "+" : "");
+ return -1;
+ }
+ if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
+ return -1;
+ if (sta->vlan_id)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+ hostapd_free_psk_list(sta->psk);
+ if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+ sta->psk = *psk;
+ *psk = NULL;
+ } else {
+ sta->psk = NULL;
+ }
+
+ os_free(sta->identity);
+ sta->identity = *identity;
+ *identity = NULL;
+
+ os_free(sta->radius_cui);
+ sta->radius_cui = *radius_cui;
+ *radius_cui = NULL;
+
+ if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+ sta->acct_interim_interval = acct_interim_interval;
+ if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
+ sta->session_timeout_set = 1;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ } else {
+ sta->session_timeout_set = 0;
+ ap_sta_no_session_timeout(hapd, sta);
+ }
+
+ return 0;
+}
+
+
static void handle_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -998,8 +1760,6 @@ static void handle_auth(struct hostapd_data *hapd,
char *radius_cui = NULL;
u16 seq_ctrl;
- os_memset(&vlan_id, 0, sizeof(vlan_id));
-
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
(unsigned long) len);
@@ -1047,20 +1807,29 @@ static void handle_auth(struct hostapd_data *hapd,
#endif /* CONFIG_NO_RC4 */
if (hapd->tkip_countermeasures) {
- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ wpa_printf(MSG_DEBUG,
+ "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
auth_alg == WLAN_AUTH_OPEN) ||
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
(hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_FT) ||
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
(hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_SAE) ||
#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_FILS_SK) ||
+ (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+ hapd->conf->fils_dh_group &&
+ auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
+#endif /* CONFIG_FILS */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) {
wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
@@ -1139,29 +1908,23 @@ static void handle_auth(struct hostapd_data *hapd,
}
}
- res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
- &session_timeout,
- &acct_interim_interval, &vlan_id,
- &psk, &identity, &radius_cui);
-
+ res = ieee802_11_allowed_address(
+ hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout,
+ &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui,
+ 0);
if (res == HOSTAPD_ACL_REJECT) {
- wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
- MAC2STR(mgmt->sa));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Authentication frame from " MACSTR
+ " due to ACL reject", MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- if (res == HOSTAPD_ACL_PENDING) {
- wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
- " waiting for an external authentication",
- MAC2STR(mgmt->sa));
- /* Authentication code will re-send the authentication frame
- * after it has received (and cached) information from the
- * external source. */
+ if (res == HOSTAPD_ACL_PENDING)
return;
- }
sta = ap_get_sta(hapd, mgmt->sa);
if (sta) {
+ sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
@@ -1203,6 +1966,7 @@ static void handle_auth(struct hostapd_data *hapd,
sta = ap_sta_add(hapd, mgmt->sa);
if (!sta) {
+ wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
@@ -1210,47 +1974,18 @@ static void handle_auth(struct hostapd_data *hapd,
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
- if (vlan_id.notempty &&
- !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
- HOSTAPD_LEVEL_INFO,
- "Invalid VLAN %d%s received from RADIUS server",
- vlan_id.untagged,
- vlan_id.tagged[0] ? "+" : "");
+ res = ieee802_11_set_radius_info(
+ hapd, sta, res, session_timeout, acct_interim_interval,
+ &vlan_id, &psk, &identity, &radius_cui);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
- }
- if (sta->vlan_id)
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
- HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
-
- hostapd_free_psk_list(sta->psk);
- if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
- sta->psk = psk;
- psk = NULL;
- } else {
- sta->psk = NULL;
- }
-
- sta->identity = identity;
- identity = NULL;
- sta->radius_cui = radius_cui;
- radius_cui = NULL;
sta->flags &= ~WLAN_STA_PREAUTH;
ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
- if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
- sta->acct_interim_interval = acct_interim_interval;
- if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
- ap_sta_session_timeout(hapd, sta, session_timeout);
- else
- ap_sta_no_session_timeout(hapd, sta);
-
/*
* If the driver supports full AP client state, add a station to the
* driver before sending authentication reply to make sure the driver
@@ -1263,8 +1998,15 @@ static void handle_auth(struct hostapd_data *hapd,
*
* In mesh mode, the station was already added to the driver when the
* NEW_PEER_CANDIDATE event is received.
+ *
+ * If PMF was negotiated for the existing association, skip this to
+ * avoid dropping the STA entry and the associated keys. This is needed
+ * to allow the original connection work until the attempt can complete
+ * (re)association, so that unprotected Authentication frame cannot be
+ * used to bypass PMF protection.
*/
if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+ (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
!(hapd->conf->mesh & MESH_ENABLED) &&
!(sta->added_unassoc)) {
/*
@@ -1274,6 +2016,7 @@ static void handle_auth(struct hostapd_data *hapd,
* updated. To handle this, station's added_unassoc flag is
* cleared once the station has completed association.
*/
+ ap_sta_set_authorized(hapd, sta, 0);
hostapd_drv_sta_remove(hapd, sta->addr);
sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
WLAN_STA_AUTHORIZED);
@@ -1305,6 +2048,9 @@ static void handle_auth(struct hostapd_data *hapd,
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
fc & WLAN_FC_ISWEP);
+ if (resp != 0)
+ wpa_printf(MSG_DEBUG,
+ "auth_shared_key() failed: status=%d", resp);
sta->auth_alg = WLAN_AUTH_SHARED_KEY;
mlme_authenticate_indication(hapd, sta);
if (sta->challenge && auth_transaction == 1) {
@@ -1316,7 +2062,7 @@ static void handle_auth(struct hostapd_data *hapd,
}
break;
#endif /* CONFIG_NO_RC4 */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
case WLAN_AUTH_FT:
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
@@ -1335,7 +2081,7 @@ static void handle_auth(struct hostapd_data *hapd,
handle_auth_ft_finish, hapd);
/* handle_auth_ft_finish() callback will complete auth. */
return;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
case WLAN_AUTH_SAE:
#ifdef CONFIG_MESH
@@ -1357,6 +2103,15 @@ static void handle_auth(struct hostapd_data *hapd,
status_code);
return;
#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ case WLAN_AUTH_FILS_SK:
+ case WLAN_AUTH_FILS_SK_PFS:
+ handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+ auth_alg, auth_transaction, status_code,
+ handle_auth_fils_finish);
+ return;
+#endif /* CONFIG_FILS */
}
fail:
@@ -1366,7 +2121,7 @@ static void handle_auth(struct hostapd_data *hapd,
reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
auth_transaction + 1, resp, resp_ies,
- resp_ies_len);
+ resp_ies_len, "handle-auth");
if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
reply_res != WLAN_STATUS_SUCCESS)) {
@@ -1459,6 +2214,11 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
{
+ /* Supported rates not used in IEEE 802.11ad/DMG */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+ return WLAN_STATUS_SUCCESS;
+
if (!elems->supp_rates) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -1496,13 +2256,188 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_INTERWORKING */
- if (ext_capab_ie_len > 0)
+ if (ext_capab_ie_len > 0) {
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+ os_free(sta->ext_capability);
+ sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+ if (sta->ext_capability) {
+ sta->ext_capability[0] = ext_capab_ie_len;
+ os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+ ext_capab_ie_len);
+ }
+ }
return WLAN_STATUS_SUCCESS;
}
+#ifdef CONFIG_OWE
+
+static int owe_group_supported(struct hostapd_data *hapd, u16 group)
+{
+ int i;
+ int *groups = hapd->conf->owe_groups;
+
+ if (group != 19 && group != 20 && group != 21)
+ return 0;
+
+ if (!groups)
+ return 1;
+
+ for (i = 0; groups[i] > 0; i++) {
+ if (groups[i] == group)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static u16 owe_process_assoc_req(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *owe_dh,
+ u8 owe_dh_len)
+{
+ struct wpabuf *secret, *pub, *hkey;
+ int res;
+ u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
+ const char *info = "OWE Key Generation";
+ const u8 *addr[2];
+ size_t len[2];
+ u16 group;
+ size_t hash_len, prime_len;
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ group = WPA_GET_LE16(owe_dh);
+ if (!owe_group_supported(hapd, group)) {
+ wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ }
+ if (group == 19)
+ prime_len = 32;
+ else if (group == 20)
+ prime_len = 48;
+ else if (group == 21)
+ prime_len = 66;
+ else
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+ crypto_ecdh_deinit(sta->owe_ecdh);
+ sta->owe_ecdh = crypto_ecdh_init(group);
+ if (!sta->owe_ecdh)
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ sta->owe_group = group;
+
+ secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+ owe_dh_len - 2);
+ secret = wpabuf_zeropad(secret, prime_len);
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+ /* prk = HKDF-extract(C | A | group, z) */
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ /* PMKID = Truncate-128(Hash(C | A)) */
+ addr[0] = owe_dh + 2;
+ len[0] = owe_dh_len - 2;
+ addr[1] = wpabuf_head(pub);
+ len[1] = wpabuf_len(pub);
+ if (group == 19) {
+ res = sha256_vector(2, addr, len, pmkid);
+ hash_len = SHA256_MAC_LEN;
+ } else if (group == 20) {
+ res = sha384_vector(2, addr, len, pmkid);
+ hash_len = SHA384_MAC_LEN;
+ } else if (group == 21) {
+ res = sha512_vector(2, addr, len, pmkid);
+ hash_len = SHA512_MAC_LEN;
+ } else {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ pub = wpabuf_zeropad(pub, prime_len);
+ if (res < 0 || !pub) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+ if (!hkey) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+ wpabuf_put_buf(hkey, pub); /* A */
+ wpabuf_free(pub);
+ wpabuf_put_le16(hkey, group); /* group */
+ if (group == 19)
+ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 20)
+ res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ else if (group == 21)
+ res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ wpabuf_clear_free(hkey);
+ wpabuf_clear_free(secret);
+ if (res < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
+
+ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = os_malloc(hash_len);
+ if (!sta->owe_pmk) {
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (group == 19)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ else if (group == 20)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ else if (group == 21)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, hash_len);
+ os_memset(prk, 0, SHA512_MAC_LEN);
+ if (res < 0) {
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ sta->owe_pmk_len = hash_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
+ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+ wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+#endif /* CONFIG_OWE */
+
+
static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
{
@@ -1644,32 +2579,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
wpa_ie, wpa_ie_len,
- elems.mdie, elems.mdie_len);
- if (res == WPA_INVALID_GROUP)
- resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
- else if (res == WPA_INVALID_PAIRWISE)
- resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
- else if (res == WPA_INVALID_AKMP)
- resp = WLAN_STATUS_AKMP_NOT_VALID;
- else if (res == WPA_ALLOC_FAIL)
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-#ifdef CONFIG_IEEE80211W
- else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
- else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
- resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
-#endif /* CONFIG_IEEE80211W */
- else if (res == WPA_INVALID_MDIE)
- resp = WLAN_STATUS_INVALID_MDIE;
- else if (res != WPA_IE_OK)
- resp = WLAN_STATUS_INVALID_IE;
+ elems.mdie, elems.mdie_len,
+ elems.owe_dh, elems.owe_dh_len);
+ resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
#ifdef CONFIG_IEEE80211W
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
sta->sa_query_count > 0)
ap_check_sa_query_timeout(hapd, sta);
- if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) ==
+ (WLAN_STA_ASSOC | WLAN_STA_MFP) &&
+ !sta->sa_query_timed_out &&
(!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
/*
* STA has already been associated with MFP and SA
@@ -1690,7 +2613,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
sta->flags &= ~WLAN_STA_MFP;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
if (!reassoc) {
wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
@@ -1705,9 +2628,13 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
+ sta->sae->state == SAE_ACCEPTED)
+ wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
+
if (wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg == WLAN_AUTH_OPEN) {
struct rsn_pmksa_cache_entry *sa;
@@ -1731,6 +2658,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems.owe_dh) {
+ resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
+ elems.owe_dh_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_OWE */
+
#ifdef CONFIG_IEEE80211N
if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -1779,6 +2717,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems.hs20_len - 4);
} else
sta->hs20_ie = NULL;
+
+ wpabuf_free(sta->roaming_consortium);
+ if (elems.roaming_cons_sel)
+ sta->roaming_consortium = wpabuf_alloc_copy(
+ elems.roaming_cons_sel + 4,
+ elems.roaming_cons_sel_len - 4);
+ else
+ sta->roaming_consortium = NULL;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
@@ -1810,6 +2756,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
sizeof(sta->rrm_enabled_capa));
+ if (elems.power_capab) {
+ sta->min_tx_power = elems.power_capab[0];
+ sta->max_tx_power = elems.power_capab[1];
+ sta->power_capab = 1;
+ } else {
+ sta->power_capab = 0;
+ }
+
return WLAN_STATUS_SUCCESS;
}
@@ -1856,7 +2810,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
*/
if (!sta->added_unassoc &&
(!(sta->flags & WLAN_STA_AUTHORIZED) ||
- !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) {
+ (!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;
@@ -1904,21 +2859,36 @@ static int add_associated_sta(struct hostapd_data *hapd,
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
- u16 status_code, int reassoc, const u8 *ies,
- size_t ies_len)
+ const u8 *addr, u16 status_code, int reassoc,
+ const u8 *ies, size_t ies_len)
{
int send_len;
- u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+ u8 *buf;
+ size_t buflen;
struct ieee80211_mgmt *reply;
u8 *p;
+ u16 res = WLAN_STATUS_SUCCESS;
- os_memset(buf, 0, sizeof(buf));
+ buflen = sizeof(struct ieee80211_mgmt) + 1024;
+#ifdef CONFIG_FILS
+ if (sta && sta->fils_hlp_resp)
+ buflen += wpabuf_len(sta->fils_hlp_resp);
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ buflen += 150;
+#endif /* CONFIG_OWE */
+ buf = os_zalloc(buflen);
+ if (!buf) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
reply = (struct ieee80211_mgmt *) buf;
reply->frame_control =
IEEE80211_FC(WLAN_FC_TYPE_MGMT,
(reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
WLAN_FC_STYPE_ASSOC_RESP));
- os_memcpy(reply->da, sta->addr, ETH_ALEN);
+ os_memcpy(reply->da, addr, ETH_ALEN);
os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
@@ -1927,24 +2897,40 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
reply->u.assoc_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd));
reply->u.assoc_resp.status_code = host_to_le16(status_code);
- reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
+
+ reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
+ BIT(14) | BIT(15));
/* Supported rates */
p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
-#ifdef CONFIG_IEEE80211R
- if (status_code == WLAN_STATUS_SUCCESS) {
+#ifdef CONFIG_IEEE80211R_AP
+ if (sta && status_code == WLAN_STATUS_SUCCESS) {
/* IEEE 802.11r: Mobility Domain Information, Fast BSS
* Transition Information, RSN, [RIC Response] */
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
- buf + sizeof(buf) - p,
+ buf + buflen - p,
sta->auth_alg, ies, ies_len);
+ if (!p) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to write AssocResp IEs");
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_OWE
+ if (sta && status_code == WLAN_STATUS_SUCCESS &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
+ buf + buflen - p,
+ ies, ies_len);
+#endif /* CONFIG_OWE */
#ifdef CONFIG_IEEE80211W
- if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+ if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
#endif /* CONFIG_IEEE80211W */
@@ -1957,7 +2943,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
u32 nsts = 0, sta_nsts;
- if (hapd->conf->use_sta_nsts && sta->vht_capabilities) {
+ if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
struct ieee80211_vht_capabilities *capa;
nsts = (hapd->iface->conf->vht_capab >>
@@ -1978,7 +2964,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = hostapd_eid_ext_capab(hapd, p);
p = hostapd_eid_bss_max_idle_period(hapd, p);
- if (sta->qos_map_enabled)
+ if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
#ifdef CONFIG_FST
@@ -1990,16 +2976,17 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_FST */
#ifdef CONFIG_IEEE80211AC
- if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+ if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
p = hostapd_eid_vendor_vht(hapd, p);
#endif /* CONFIG_IEEE80211AC */
- if (sta->flags & WLAN_STA_WMM)
+ if (sta && (sta->flags & WLAN_STA_WMM))
p = hostapd_eid_wmm(hapd, p);
#ifdef CONFIG_WPS
- if ((sta->flags & WLAN_STA_WPS) ||
- ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
+ if (sta &&
+ ((sta->flags & WLAN_STA_WPS) ||
+ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
struct wpabuf *wps = wps_build_assoc_resp_ie();
if (wps) {
os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
@@ -2010,7 +2997,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
- if (sta->p2p_ie && hapd->p2p_group) {
+ if (sta && sta->p2p_ie && hapd->p2p_group) {
struct wpabuf *p2p_resp_ie;
enum p2p_status_code status;
switch (status_code) {
@@ -2039,10 +3026,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = hostapd_eid_p2p_manage(hapd, p);
#endif /* CONFIG_P2P_MANAGER */
- p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
+ p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
if (hapd->conf->assocresp_elements &&
- (size_t) (buf + sizeof(buf) - p) >=
+ (size_t) (buf + buflen - p) >=
wpabuf_len(hapd->conf->assocresp_elements)) {
os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
wpabuf_len(hapd->conf->assocresp_elements));
@@ -2051,16 +3038,176 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
send_len += p - reply->u.assoc_resp.variable;
+#ifdef CONFIG_FILS
+ if (sta &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+ status_code == WLAN_STATUS_SUCCESS) {
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+ ParseFailed || !elems.fils_session) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+
+ /* FILS Session */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + FILS_SESSION_LEN; /* Length */
+ *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+ os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
+ send_len += 2 + 1 + FILS_SESSION_LEN;
+
+ send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
+ buflen, sta->fils_hlp_resp);
+ if (send_len < 0) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ }
+#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));
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- return WLAN_STATUS_SUCCESS;
+done:
+ os_free(buf);
+ return res;
}
+#ifdef CONFIG_OWE
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *reason)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->own_ie_override) {
+ wpa_printf(MSG_DEBUG, "OWE: Using IE override");
+ *reason = WLAN_STATUS_SUCCESS;
+ return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+ *reason = WLAN_STATUS_SUCCESS;
+ return owe_buf;
+ }
+
+ *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
+ if (*reason != WLAN_STATUS_SUCCESS)
+ return NULL;
+
+ owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
+ owe_buf_len, NULL, 0);
+
+ if (sta->owe_ecdh && owe_buf) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ *reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return owe_buf;
+ }
+
+ /* OWE Diffie-Hellman Parameter element */
+ *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
+ *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
+ */
+ WPA_PUT_LE16(owe_buf, sta->owe_group);
+ owe_buf += 2;
+ os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
+ owe_buf += wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+
+ return owe_buf;
+}
+#endif /* CONFIG_OWE */
+
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ u16 reply_res;
+
+ wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
+ MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ if (!sta->fils_pending_assoc_req)
+ return;
+ 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);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ wpabuf_free(sta->hlp_dhcp_discover);
+ sta->hlp_dhcp_discover = NULL;
+
+ /*
+ * Remove the station in case transmission of a success response fails.
+ * At this point the station was already added associated to the driver.
+ */
+ if (reply_res != WLAN_STATUS_SUCCESS)
+ hostapd_drv_sta_remove(hapd, sta->addr);
+}
+
+
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = eloop_data;
+
+ wpa_printf(MSG_DEBUG,
+ "FILS: HLP response timeout - continue with association response for "
+ MACSTR, MAC2STR(sta->addr));
+ if (sta->fils_drv_assoc_finish)
+ hostapd_notify_assoc_fils_finish(hapd, sta);
+ else
+ fils_hlp_finish_assoc(hapd, sta);
+}
+
+#endif /* CONFIG_FILS */
+
+
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc)
@@ -2070,6 +3217,13 @@ static void handle_assoc(struct hostapd_data *hapd,
const u8 *pos;
int left, i;
struct sta_info *sta;
+ u8 *tmp = NULL;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
+ char *identity = NULL;
+ char *radius_cui = NULL;
+#ifdef CONFIG_FILS
+ int delay_assoc = 0;
+#endif /* CONFIG_FILS */
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -2127,7 +3281,7 @@ static void handle_assoc(struct hostapd_data *hapd,
}
sta = ap_get_sta(hapd, mgmt->sa);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
@@ -2140,24 +3294,76 @@ static void handle_assoc(struct hostapd_data *hapd,
*/
sta->flags |= WLAN_STA_AUTH;
} else
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
- hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "Station tried to "
- "associate before authentication "
- "(aid=%d flags=0x%x)",
- sta ? sta->aid : -1,
- sta ? sta->flags : 0);
- send_deauth(hapd, mgmt->sa,
- WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
- return;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
+ HOSTAPD_MODE_IEEE80211AD) {
+ int acl_res;
+ u32 session_timeout, acct_interim_interval;
+ struct vlan_description vlan_id;
+
+ acl_res = ieee802_11_allowed_address(
+ hapd, mgmt->sa, (const u8 *) mgmt, len,
+ &session_timeout, &acct_interim_interval,
+ &vlan_id, &psk, &identity, &radius_cui, 0);
+ if (acl_res == HOSTAPD_ACL_REJECT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Ignore Association Request frame from "
+ MACSTR " due to ACL reject",
+ MAC2STR(mgmt->sa));
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ if (acl_res == HOSTAPD_ACL_PENDING)
+ return;
+
+ /* DMG/IEEE 802.11ad does not use authentication.
+ * Allocate sta entry upon association. */
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Failed to add STA");
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
+
+ acl_res = ieee802_11_set_radius_info(
+ hapd, sta, acl_res, session_timeout,
+ acct_interim_interval, &vlan_id, &psk,
+ &identity, &radius_cui);
+ if (acl_res) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Skip authentication for DMG/IEEE 802.11ad");
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_OPEN;
+ } else {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Station tried to associate before authentication (aid=%d flags=0x%x)",
+ sta ? sta->aid : -1,
+ sta ? sta->flags : 0);
+ send_deauth(hapd, mgmt->sa,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+ return;
+ }
}
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
- sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
- WLAN_FC_STYPE_ASSOC_REQ) {
+ sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+ WLAN_FC_STYPE_ASSOC_REQ)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Drop repeated association frame seq_ctrl=0x%x",
@@ -2169,7 +3375,7 @@ static void handle_assoc(struct hostapd_data *hapd,
WLAN_FC_STYPE_ASSOC_REQ;
if (hapd->tkip_countermeasures) {
- resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2195,6 +3401,32 @@ static void handle_assoc(struct hostapd_data *hapd,
*/
sta->capability = capab_info;
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ int res;
+
+ /* The end of the payload is encrypted. Need to decrypt it
+ * before parsing. */
+
+ tmp = os_memdup(pos, left);
+ if (!tmp) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+
+ res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+ len, tmp, left);
+ if (res < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ pos = tmp;
+ left = res;
+ }
+#endif /* CONFIG_FILS */
+
/* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */
resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -2210,7 +3442,8 @@ static void handle_assoc(struct hostapd_data *hapd,
sta->listen_interval = listen_interval;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
sta->flags |= WLAN_STA_NONERP;
for (i = 0; i < sta->supported_rates_len; i++) {
if ((sta->supported_rates[i] & 0x7f) > 22) {
@@ -2229,7 +3462,8 @@ static void handle_assoc(struct hostapd_data *hapd,
!sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 1;
hapd->iface->num_sta_no_short_slot_time++;
- if (hapd->iface->current_mode->mode ==
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode ==
HOSTAPD_MODE_IEEE80211G &&
hapd->iface->num_sta_no_short_slot_time == 1)
ieee802_11_set_beacons(hapd->iface);
@@ -2244,7 +3478,8 @@ static void handle_assoc(struct hostapd_data *hapd,
!sta->no_short_preamble_set) {
sta->no_short_preamble_set = 1;
hapd->iface->num_sta_no_short_preamble++;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 1)
ieee802_11_set_beacons(hapd->iface);
}
@@ -2281,7 +3516,22 @@ static void handle_assoc(struct hostapd_data *hapd,
taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
#endif /* CONFIG_TAXONOMY */
+ sta->pending_wds_enable = 0;
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ if (fils_process_hlp(hapd, sta, pos, left) > 0)
+ delay_assoc = 1;
+ }
+#endif /* CONFIG_FILS */
+
fail:
+ os_free(identity);
+ os_free(radius_cui);
+ hostapd_free_psk_list(psk);
+
/*
* In case of a successful response, add the station to the driver.
* Otherwise, the kernel may ignore Data frames before we process the
@@ -2300,18 +3550,44 @@ 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 && add_associated_sta(hapd, sta))
+ if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta))
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
- reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+#ifdef CONFIG_FILS
+ if (sta) {
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ os_free(sta->fils_pending_assoc_req);
+ sta->fils_pending_assoc_req = NULL;
+ sta->fils_pending_assoc_req_len = 0;
+ wpabuf_free(sta->fils_hlp_resp);
+ sta->fils_hlp_resp = NULL;
+ }
+ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+ sta->fils_pending_assoc_req = tmp;
+ sta->fils_pending_assoc_req_len = left;
+ sta->fils_pending_assoc_is_reassoc = reassoc;
+ sta->fils_drv_assoc_finish = 0;
+ wpa_printf(MSG_DEBUG,
+ "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+ eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
+ fils_hlp_timeout, hapd, sta);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
+ reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
+ left);
+ os_free(tmp);
/*
* Remove the station in case tranmission of a success response fails
* (the STA was added associated to the driver) or if the station was
* previously added unassociated.
*/
- if ((reply_res != WLAN_STATUS_SUCCESS &&
- resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) {
+ if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
+ resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
@@ -2368,6 +3644,17 @@ static void handle_disassoc(struct hostapd_data *hapd,
mlme_disassociate_indication(
hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+
+ /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
+ * disassociation. */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ sta->flags &= ~WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+ ap_free_sta(hapd, sta);
+ }
}
@@ -2462,12 +3749,13 @@ static int robust_action_frame(u8 category)
static int handle_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ unsigned int freq)
{
struct sta_info *sta;
- sta = ap_get_sta(hapd, mgmt->sa);
+ u8 *action __maybe_unused;
- if (len < IEEE80211_HDRLEN + 1) {
+ if (len < IEEE80211_HDRLEN + 2 + 1) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"handle_action - too short payload (len=%lu)",
@@ -2475,11 +3763,19 @@ static int handle_action(struct hostapd_data *hapd,
return 0;
}
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " len %d freq %u",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
(sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
"frame (category=%u) from unassociated STA " MACSTR,
- MAC2STR(mgmt->sa), mgmt->u.action.category);
+ mgmt->u.action.category, MAC2STR(mgmt->sa));
return 0;
}
@@ -2516,14 +3812,14 @@ static int handle_action(struct hostapd_data *hapd,
}
switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
case WLAN_ACTION_FT:
if (!sta ||
wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
len - IEEE80211_HDRLEN))
break;
return 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
case WLAN_ACTION_WMM:
hostapd_wmm_action(hapd, mgmt, len);
return 1;
@@ -2531,11 +3827,11 @@ static int handle_action(struct hostapd_data *hapd,
case WLAN_ACTION_SA_QUERY:
return hostapd_sa_query_action(hapd, mgmt, len);
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
case WLAN_ACTION_WNM:
ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
return 1;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_FST
case WLAN_ACTION_FST:
if (hapd->iface->fst)
@@ -2551,12 +3847,41 @@ static int handle_action(struct hostapd_data *hapd,
if (len >= IEEE80211_HDRLEN + 2 &&
mgmt->u.action.u.public_action.action ==
WLAN_PA_20_40_BSS_COEX) {
- wpa_printf(MSG_DEBUG,
- "HT20/40 coex mgmt frame received from STA "
- MACSTR, MAC2STR(mgmt->sa));
hostapd_2040_coex_action(hapd, mgmt, len);
+ return 1;
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_DPP
+ if (len >= IEEE80211_HDRLEN + 6 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.oui;
+ end = ((const u8 *) mgmt) + len;
+ hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
+ freq);
+ return 1;
+ }
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_INITIAL_RESP ||
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_COMEBACK_RESP)) {
+ const u8 *pos, *end;
+
+ pos = &mgmt->u.action.u.public_action.action;
+ end = ((const u8 *) mgmt) + len;
+ gas_query_ap_rx(hapd->gas, mgmt->sa,
+ mgmt->u.action.category,
+ pos, end - pos, hapd->iface->freq);
+ return 1;
+ }
+#endif /* CONFIG_DPP */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len,
@@ -2600,10 +3925,9 @@ static int handle_action(struct hostapd_data *hapd,
*/
wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
"frame back to sender");
- resp = os_malloc(len);
+ resp = os_memdup(mgmt, len);
if (resp == NULL)
return 0;
- os_memcpy(resp, mgmt, len);
os_memcpy(resp->da, resp->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
@@ -2639,10 +3963,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct ieee80211_mgmt *mgmt;
u16 fc, stype;
int ret = 0;
+ unsigned int freq;
+ int ssi_signal = fi ? fi->ssi_signal : 0;
if (len < 24)
return 0;
+ if (fi && fi->freq)
+ freq = fi->freq;
+ else
+ freq = hapd->iface->freq;
+
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
@@ -2669,11 +4000,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
- handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
+ handle_probe_req(hapd, mgmt, len, ssi_signal);
return 1;
}
- if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+ if ((!is_broadcast_ether_addr(mgmt->da) ||
+ stype != WLAN_FC_STYPE_ACTION) &&
+ os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
@@ -2682,7 +4015,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
}
if (hapd->iconf->track_sta_max_num)
- sta_track_add(hapd->iface, mgmt->sa);
+ sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
switch (stype) {
case WLAN_FC_STYPE_AUTH:
@@ -2712,7 +4045,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action");
- ret = handle_action(hapd, mgmt, len);
+ ret = handle_action(hapd, mgmt, len, freq);
break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -2734,7 +4067,8 @@ static void handle_auth_cb(struct hostapd_data *hapd,
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
- wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+ wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
+ " not found",
MAC2STR(mgmt->da));
return;
}
@@ -2856,11 +4190,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
new_assoc = 0;
sta->flags |= WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+ !hapd->conf->osen) ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK ||
sta->auth_alg == WLAN_AUTH_FT) {
/*
- * Open, static WEP, or FT protocol; no separate authorization
- * step.
+ * Open, static WEP, FT protocol, or FILS; no separate
+ * authorization step.
*/
ap_sta_set_authorized(hapd, sta, 1);
}
@@ -2874,16 +4212,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
sta->sa_query_timed_out = 0;
#endif /* CONFIG_IEEE80211W */
- if (sta->flags & WLAN_STA_WDS) {
- int ret;
- char ifname_wds[IFNAMSIZ + 1];
-
- ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
- sta->aid, 1);
- if (!ret)
- hostapd_set_wds_encryption(hapd, sta, ifname_wds);
- }
-
if (sta->eapol_sm == NULL) {
/*
* This STA does not use RADIUS server for EAP authentication,
@@ -2900,6 +4228,27 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
hostapd_set_sta_flags(hapd, sta);
+ if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
+ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
+ MACSTR " based on pending request",
+ MAC2STR(sta->addr));
+ sta->pending_wds_enable = 0;
+ sta->flags |= WLAN_STA_WDS;
+ }
+
+ if (sta->flags & WLAN_STA_WDS) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+
+ wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
+ MACSTR " (aid %u)",
+ MAC2STR(sta->addr), sta->aid);
+ ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+ sta->aid, 1);
+ if (!ret)
+ hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+ }
+
if (sta->auth_alg == WLAN_AUTH_FT)
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
else
@@ -2907,6 +4256,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+#ifdef CONFIG_FILS
+ if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+ fils_set_tk(sta->wpa_sm) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ return;
+ }
+#endif /* CONFIG_FILS */
+
if (sta->pending_eapol_rx) {
struct os_reltime now, age;
@@ -2976,6 +4337,65 @@ static void handle_disassoc_cb(struct hostapd_data *hapd,
}
+static void handle_action_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ const struct rrm_measurement_report_element *report;
+
+ if (is_multicast_ether_addr(mgmt->da))
+ return;
+#ifdef CONFIG_DPP
+ if (len >= IEEE80211_HDRLEN + 6 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ DPP_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = &mgmt->u.action.u.vs_public_action.variable[1];
+ end = ((const u8 *) mgmt) + len;
+ hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
+ return;
+ }
+ if (len >= IEEE80211_HDRLEN + 2 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_INITIAL_REQ ||
+ mgmt->u.action.u.public_action.action ==
+ WLAN_PA_GAS_COMEBACK_REQ)) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.public_action.variable;
+ end = ((const u8 *) mgmt) + len;
+ gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
+ return;
+ }
+#endif /* CONFIG_DPP */
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (len < 24 + 5 + sizeof(*report))
+ return;
+ report = (const struct rrm_measurement_report_element *)
+ &mgmt->u.action.u.rrm.variable[2];
+ if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
+ mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
+ report->eid == WLAN_EID_MEASURE_REQUEST &&
+ report->len >= 3 &&
+ report->type == MEASURE_TYPE_BEACON)
+ hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+}
+
+
/**
* ieee802_11_mgmt_cb - Process management frame TX status callback
* @hapd: hostapd BSS data structure (the BSS from which the management frame
@@ -2993,8 +4413,16 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->ext_mgmt_frame_handling) {
- wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
- stype, ok);
+ size_t hex_len = 2 * len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "MGMT-TX-STATUS stype=%u ok=%d buf=%s",
+ stype, ok, hex);
+ os_free(hex);
+ }
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
@@ -3025,6 +4453,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
+ handle_action_cb(hapd, mgmt, len, ok);
break;
default:
wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
@@ -3139,10 +4568,22 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
struct sta_info *sta;
sta = ap_get_sta(hapd, src);
- if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ if (sta &&
+ ((sta->flags & WLAN_STA_ASSOC) ||
+ ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
if (!hapd->conf->wds_sta)
return;
+ if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
+ WLAN_STA_ASSOC_REQ_OK) {
+ wpa_printf(MSG_DEBUG,
+ "Postpone 4-address WDS mode enabling for STA "
+ MACSTR " since TX status for AssocResp is not yet known",
+ MAC2STR(sta->addr));
+ sta->pending_wds_enable = 1;
+ return;
+ }
+
if (wds && !(sta->flags & WLAN_STA_WDS)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 0327dec2a2bc..2f3b4da8e752 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -16,6 +16,8 @@ struct hostapd_frame_info;
struct ieee80211_ht_capabilities;
struct ieee80211_vht_capabilities;
struct ieee80211_mgmt;
+struct vlan_description;
+struct hostapd_sta_wpa_psk_short;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -55,6 +57,8 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
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);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -135,4 +139,31 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len);
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+ struct sta_info *sta, int success,
+ struct wpabuf *erp_resp,
+ const u8 *msk, size_t msk_len);
+u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *owe_dh, u8 owe_dh_len,
+ u8 *owe_buf, size_t owe_buf_len, u16 *reason);
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *pos, size_t len, u16 auth_alg,
+ u16 auth_transaction, u16 status_code,
+ void (*cb)(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ u16 resp, struct wpabuf *data, int pub));
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
+int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *msg, size_t len, u32 *session_timeout,
+ u32 *acct_interim_interval,
+ struct vlan_description *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui,
+ int is_probe_req);
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index b8905373618d..5cb7fb1454f7 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -244,6 +244,7 @@ int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
* @psk: Linked list buffer for returning WPA PSK
* @identity: Buffer for returning identity (from RADIUS)
* @radius_cui: Buffer for returning CUI (from RADIUS)
+ * @is_probe_req: Whether this query for a Probe Request frame
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
*
* The caller is responsible for freeing the returned *identity and *radius_cui
@@ -254,7 +255,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui)
+ char **identity, char **radius_cui,
+ int is_probe_req)
{
int res;
@@ -281,6 +283,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
+ if (is_probe_req) {
+ /* Skip RADIUS queries for Probe Request frames to avoid
+ * excessive load on the authentication server. */
+ return HOSTAPD_ACL_ACCEPT;
+ };
+
/* 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,
@@ -327,14 +335,13 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
return HOSTAPD_ACL_REJECT;
}
- query->auth_msg = os_malloc(len);
+ query->auth_msg = os_memdup(msg, len);
if (query->auth_msg == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
"auth frame.");
hostapd_acl_query_free(query);
return HOSTAPD_ACL_REJECT;
}
- os_memcpy(query->auth_msg, msg, len);
query->auth_msg_len = len;
query->next = hapd->acl_queries;
hapd->acl_queries = query;
@@ -665,9 +672,11 @@ void hostapd_acl_deinit(struct hostapd_data *hapd)
#ifndef CONFIG_NO_RADIUS
hostapd_acl_cache_free(hapd->acl_cache);
+ hapd->acl_cache = NULL;
#endif /* CONFIG_NO_RADIUS */
query = hapd->acl_queries;
+ hapd->acl_queries = NULL;
while (query) {
prev = query;
query = query->next;
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 71f53b9612fa..5aece5183c69 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -23,7 +23,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
u32 *acct_interim_interval,
struct vlan_description *vlan_id,
struct hostapd_sta_wpa_psk_short **psk,
- char **identity, char **radius_cui);
+ char **identity, char **radius_cui,
+ int is_probe_req);
int hostapd_acl_init(struct hostapd_data *hapd);
void hostapd_acl_deinit(struct hostapd_data *hapd);
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
new file mode 100644
index 000000000000..1a8d46972985
--- /dev/null
+++ b/src/ap/ieee802_11_he.c
@@ -0,0 +1,88 @@
+/*
+ * hostapd / IEEE 802.11ax HE
+ * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_capabilities *cap;
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(struct ieee80211_he_capabilities);
+ *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
+
+ cap = (struct ieee80211_he_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+
+ if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
+ cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
+ HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+
+ if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
+ cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
+ HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_operation *oper;
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(struct ieee80211_he_operation);
+ *pos++ = WLAN_EID_EXT_HE_OPERATION;
+
+ oper = (struct ieee80211_he_operation *) pos;
+ os_memset(oper, 0, sizeof(*oper));
+
+ if (hapd->iface->conf->he_op.he_bss_color)
+ oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color;
+
+ if (hapd->iface->conf->he_op.he_default_pe_duration)
+ oper->he_oper_params |=
+ (hapd->iface->conf->he_op.he_default_pe_duration <<
+ HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+
+ if (hapd->iface->conf->he_op.he_twt_required)
+ oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED;
+
+ if (hapd->iface->conf->he_op.he_rts_threshold)
+ oper->he_oper_params |=
+ (hapd->iface->conf->he_op.he_rts_threshold <<
+ HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+ /* TODO: conditional MaxBSSID Indicator subfield */
+
+ pos += sizeof(*oper);
+
+ return pos;
+}
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 5eb1060a2965..214855dccb8c 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -236,17 +236,29 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
int i;
const u8 *start = (const u8 *) mgmt;
const u8 *data = start + IEEE80211_HDRLEN + 2;
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG,
+ "HT: Received 20/40 BSS Coexistence Management frame from "
+ MACSTR, MAC2STR(mgmt->sa));
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
mgmt->u.action.u.public_action.action);
- if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled");
return;
+ }
- if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore too short 20/40 BSS Coexistence Management frame");
return;
+ }
+ /* 20/40 BSS Coexistence element */
bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
bc_ie->length < 1) {
@@ -254,13 +266,35 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
bc_ie->element_id, bc_ie->length);
return;
}
- if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Coexistence element");
return;
+ }
data += 2 + bc_ie->length;
- wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
- bc_ie->coex_param);
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)",
+ bc_ie->coex_param,
+ (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "",
+ (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "",
+ (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "",
+ (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "",
+ (bc_ie->coex_param & BIT(4)) ?
+ "[OBSSScanExemptionGrant]" : "",
+ (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ?
+ "[Reserved]" : "");
+
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+ /* Intra-BSS communication prohibiting 20/40 MHz BSS operation
+ */
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA");
+ return;
+ }
+
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -269,6 +303,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
}
if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+ /* Inter-BSS communication prohibiting 20/40 MHz BSS operation
+ */
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -276,12 +312,16 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
is_ht40_allowed = 0;
}
- if (start + len - data >= 3 &&
- data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+ /* 20/40 BSS Intolerant Channel Report element (zero or more times) */
+ while (start + len - data >= 3 &&
+ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
u8 ielen = data[1];
- if (ielen > start + len - data - 2)
+ if (ielen > start + len - data - 2) {
+ wpa_printf(MSG_DEBUG,
+ "Truncated 20/40 BSS Intolerant Channel Report element");
return;
+ }
ic_report = (struct ieee80211_2040_intol_chan_report *) data;
wpa_printf(MSG_DEBUG,
"20/40 BSS Intolerant Channel Report: Operating Class %u",
@@ -292,8 +332,10 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
for (i = 0; i < ielen - 1; i++) {
u8 chan = ic_report->variable[i];
+ if (chan == iface->conf->channel)
+ continue; /* matching own primary channel */
if (is_40_allowed(iface, chan))
- continue;
+ continue; /* not within affected channels */
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -301,6 +343,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd,
chan);
is_ht40_allowed = 0;
}
+
+ data += 2 + ielen;
}
wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
is_ht40_allowed, iface->num_sta_ht40_intolerant);
@@ -340,8 +384,8 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
* that did not specify a valid WMM IE in the (Re)Association Request
* frame.
*/
- if (!ht_capab ||
- !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
+ if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
+ !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
sta->flags &= ~WLAN_STA_HT;
os_free(sta->ht_capabilities);
sta->ht_capabilities = NULL;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 259413bd12ff..49e9bf8cc7c9 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -178,6 +178,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
case 1: /* Bits 8-15 */
if (hapd->conf->proxy_arp)
*pos |= 0x10; /* Bit 12 - Proxy ARP */
+ if (hapd->conf->coloc_intf_reporting) {
+ /* Bit 13 - Collocated Interference Reporting */
+ *pos |= 0x20;
+ }
break;
case 2: /* Bits 16-23 */
if (hapd->conf->wnm_sleep_mode)
@@ -186,9 +190,9 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
*pos |= 0x08; /* Bit 19 - BSS Transition */
break;
case 3: /* Bits 24-31 */
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
*pos |= 0x02; /* Bit 25 - SSID List */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
if (hapd->conf->time_advertisement == 2)
*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
if (hapd->conf->interworking)
@@ -218,12 +222,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
if (hapd->conf->ssid.utf8_ssid)
*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
break;
+ case 7: /* Bits 56-63 */
+ break;
case 8: /* Bits 64-71 */
if (hapd->conf->ftm_responder)
*pos |= 0x40; /* Bit 70 - FTM responder */
if (hapd->conf->ftm_initiator)
*pos |= 0x80; /* Bit 71 - FTM initiator */
break;
+ case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
+ wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+ *pos |= 0x01;
+#endif /* CONFIG_FILS */
+ break;
}
}
@@ -246,10 +259,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
if (len < 9 &&
(hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
len = 9;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
if (len < 4)
len = 4;
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_HS20
if (hapd->conf->hs20 && len < 6)
len = 6;
@@ -258,6 +271,11 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
if (hapd->conf->mbo_enabled && len < 6)
len = 6;
#endif /* CONFIG_MBO */
+#ifdef CONFIG_FILS
+ if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
+ len = 10;
+#endif /* CONFIG_FILS */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@@ -432,7 +450,7 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
{
size_t len;
- if (hapd->conf->time_advertisement != 2)
+ if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone)
return eid;
len = os_strlen(hapd->conf->time_zone);
@@ -503,7 +521,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
if (hapd->conf->ap_max_inactivity > 0) {
unsigned int val;
*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
@@ -521,7 +539,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
pos += 2;
*pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
}
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
return pos;
}
@@ -531,23 +549,38 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
{
- u8 mbo[6], *mbo_pos = mbo;
+ u8 mbo[9], *mbo_pos = mbo;
u8 *pos = eid;
- if (!hapd->conf->mbo_enabled)
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return eid;
- *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
- *mbo_pos++ = 1;
- /* Not Cellular aware */
- *mbo_pos++ = 0;
+ if (hapd->conf->mbo_enabled) {
+ *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+ *mbo_pos++ = 1;
+ /* Not Cellular aware */
+ *mbo_pos++ = 0;
+ }
- if (hapd->mbo_assoc_disallow) {
+ if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
*mbo_pos++ = 1;
*mbo_pos++ = hapd->mbo_assoc_disallow;
}
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
+ u8 ctrl;
+
+ ctrl = OCE_RELEASE;
+ if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
+ ctrl |= OCE_IS_STA_CFON;
+
+ *mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = ctrl;
+ }
+
pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
return pos;
@@ -556,19 +589,91 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
{
- if (!hapd->conf->mbo_enabled)
+ u8 len;
+
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return 0;
/*
* MBO IE header (6) + Capability Indication attribute (3) +
* Association Disallowed attribute (3) = 12
*/
- return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+ len = 6;
+ if (hapd->conf->mbo_enabled)
+ len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+
+ /* OCE capability indication attribute (3) */
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
+ len += 3;
+
+ return len;
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OWE
+static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd)
+{
+ return hapd->conf->owe_transition_ssid_len > 0 &&
+ !is_zero_ether_addr(hapd->conf->owe_transition_bssid);
+}
+#endif /* CONFIG_OWE */
+
+
+size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd)
+{
+#ifdef CONFIG_OWE
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return 0;
+ return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len;
+#else /* CONFIG_OWE */
+ return 0;
+#endif /* CONFIG_OWE */
+}
+
+
+u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+#ifdef CONFIG_OWE
+ u8 *pos = eid;
+ size_t elen;
+
+ if (hapd->conf->owe_transition_ifname[0] &&
+ !hostapd_eid_owe_trans_enabled(hapd))
+ hostapd_owe_trans_get_info(hapd);
+
+ if (!hostapd_eid_owe_trans_enabled(hapd))
+ return pos;
+
+ elen = hostapd_eid_owe_trans_len(hapd);
+ if (len < elen) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Not enough room in the buffer for OWE IE");
+ return pos;
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = elen - 2;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = OWE_OUI_TYPE;
+ os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ *pos++ = hapd->conf->owe_transition_ssid_len;
+ os_memcpy(pos, hapd->conf->owe_transition_ssid,
+ hapd->conf->owe_transition_ssid_len);
+ pos += hapd->conf->owe_transition_ssid_len;
+
+ return pos;
+#else /* CONFIG_OWE */
+ return eid;
+#endif /* CONFIG_OWE */
+}
+
+
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len)
@@ -584,3 +689,66 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta,
os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
supp_op_classes_len);
}
+
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_FILS
+ u8 *len;
+ u16 fils_info = 0;
+ size_t realms;
+ struct fils_realm *realm;
+
+ if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+ !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+ return pos;
+
+ realms = dl_list_len(&hapd->conf->fils_realms);
+ if (realms > 7)
+ realms = 7; /* 3 bit count field limits this to max 7 */
+
+ *pos++ = WLAN_EID_FILS_INDICATION;
+ len = pos++;
+ /* TODO: B0..B2: Number of Public Key Identifiers */
+ if (hapd->conf->erp_domain) {
+ /* B3..B5: Number of Realm Identifiers */
+ fils_info |= realms << 3;
+ }
+ /* TODO: B6: FILS IP Address Configuration */
+ if (hapd->conf->fils_cache_id_set)
+ fils_info |= BIT(7);
+ if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
+ fils_info |= BIT(8); /* HESSID Included */
+ /* FILS Shared Key Authentication without PFS Supported */
+ fils_info |= BIT(9);
+ if (hapd->conf->fils_dh_group) {
+ /* FILS Shared Key Authentication with PFS Supported */
+ fils_info |= BIT(10);
+ }
+ /* TODO: B11: FILS Public Key Authentication Supported */
+ /* B12..B15: Reserved */
+ WPA_PUT_LE16(pos, fils_info);
+ pos += 2;
+ if (hapd->conf->fils_cache_id_set) {
+ os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
+ pos += FILS_CACHE_ID_LEN;
+ }
+ if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
+ list) {
+ if (realms == 0)
+ break;
+ realms--;
+ os_memcpy(pos, realm->hash, 2);
+ pos += 2;
+ }
+ *len = pos - len - 1;
+#endif /* CONFIG_FILS */
+
+ return pos;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index f30f63bc5709..8d0662078bcf 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -334,7 +334,7 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
{
/* Disable VHT caps for STAs associated to no-VHT BSSes. */
if (!vht_capab ||
- hapd->conf->disable_11ac ||
+ !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
!check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
sta->flags &= ~WLAN_STA_VHT;
os_free(sta->vht_capabilities);
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 80ff996948f9..185279f9e3ef 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -31,6 +31,8 @@
#include "ap_drv_ops.h"
#include "wps_hostapd.h"
#include "hs20.h"
+/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
+#include "ieee802_11.h"
#include "ieee802_1x.h"
@@ -316,6 +318,7 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
hdr->code != EAP_CODE_INITIATE))
return;
+ eap_erp_update_identity(sm->eap, eap, len);
identity = eap_get_identity(sm->eap, &identity_len);
if (identity == NULL)
return;
@@ -472,7 +475,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
}
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
sta->wpa_sm &&
(wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
@@ -485,7 +488,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
return -1;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
@@ -588,9 +591,9 @@ int add_common_radius_attr(struct hostapd_data *hapd,
}
-static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *eap, size_t len)
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len)
{
struct radius_msg *msg;
struct eapol_state_machine *sm = sta->eapol_sm;
@@ -680,6 +683,8 @@ static 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 */
if (!radius_msg_add_wfa(
msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
&ver, 1)) {
@@ -709,6 +714,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
goto fail;
}
}
+
+ if (sta->roaming_consortium &&
+ !radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM,
+ wpabuf_head(sta->roaming_consortium),
+ wpabuf_len(sta->roaming_consortium))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Roaming Consortium");
+ goto fail;
+ }
+
+ if (hapd->conf->t_c_filename) {
+ be32 timestamp;
+
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME,
+ (const u8 *) hapd->conf->t_c_filename,
+ os_strlen(hapd->conf->t_c_filename))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 T&C Filename");
+ goto fail;
+ }
+
+ timestamp = host_to_be32(hapd->conf->t_c_timestamp);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP,
+ (const u8 *) ×tamp,
+ sizeof(timestamp))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add HS 2.0 Timestamp");
+ goto fail;
+ }
+ }
}
#endif /* CONFIG_HS20 */
@@ -845,7 +885,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
}
-static struct eapol_state_machine *
+struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
{
int flags = 0;
@@ -970,7 +1010,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
}
key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ if (key_mgmt != -1 &&
+ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+ key_mgmt == WPA_KEY_MGMT_DPP)) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
"STA is using PSK");
return;
@@ -1113,7 +1155,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
}
key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
- if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ if (key_mgmt != -1 &&
+ (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE ||
+ key_mgmt == WPA_KEY_MGMT_DPP)) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
/*
* Clear any possible EAPOL authenticator state to support
@@ -1154,7 +1198,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
sta->eapol_sm->eap_if->portEnabled = TRUE;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG,
@@ -1170,10 +1214,32 @@ 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);
- /* TODO: get vlan_id from R0KH using RRB message */
+ ap_sta_bind_vlan(hapd, sta);
return;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from FILS - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing FILS information. */
+ sta->eapol_sm->keyRun = TRUE;
+ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
+ sta->eapol_sm->portValid = TRUE;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ return;
+ }
+#endif /* CONFIG_FILS */
pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
if (pmksa) {
@@ -1395,11 +1461,10 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
}
} while (class_len < 1);
- nclass[nclass_count].data = os_malloc(class_len);
+ nclass[nclass_count].data = os_memdup(attr_class, class_len);
if (nclass[nclass_count].data == NULL)
break;
- os_memcpy(nclass[nclass_count].data, attr_class, class_len);
nclass[nclass_count].len = class_len;
nclass_count++;
}
@@ -1559,6 +1624,33 @@ static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
ap_sta_session_warning_timeout(hapd, sta, warning_time);
}
+
+static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len)
+{
+ if (len < 4)
+ return; /* Malformed information */
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
+ pos[0], pos[1], pos[2], pos[3]);
+ hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
+}
+
+
+static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos, size_t len)
+{
+ os_free(sta->t_c_url);
+ sta->t_c_url = os_malloc(len + 1);
+ if (!sta->t_c_url)
+ return;
+ os_memcpy(sta->t_c_url, pos, len);
+ sta->t_c_url[len] = '\0';
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: Terms and Conditions URL %s", sta->t_c_url);
+}
+
#endif /* CONFIG_HS20 */
@@ -1606,6 +1698,12 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
session_timeout);
break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
+ ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
+ ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
+ break;
}
}
#endif /* CONFIG_HS20 */
@@ -1663,6 +1761,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
struct sta_info *sta;
u32 session_timeout = 0, termination_action, acct_interim_interval;
int session_timeout_set;
+ u32 reason_code;
struct eapol_state_machine *sm;
int override_eapReq = 0;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
@@ -1788,14 +1887,17 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
break;
sta->session_timeout_set = !!session_timeout_set;
- sta->session_timeout = session_timeout;
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
/* RFC 3580, Ch. 3.17 */
if (session_timeout_set && termination_action ==
- RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
+ RADIUS_TERMINATION_ACTION_RADIUS_REQUEST)
sm->reAuthPeriod = session_timeout;
- } else if (session_timeout_set)
+ else if (session_timeout_set)
ap_sta_session_timeout(hapd, sta, session_timeout);
+ else
+ ap_sta_no_session_timeout(hapd, sta);
sm->eap_if->aaaSuccess = TRUE;
override_eapReq = 1;
@@ -1811,6 +1913,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
case RADIUS_CODE_ACCESS_REJECT:
sm->eap_if->aaaFail = TRUE;
override_eapReq = 1;
+ if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE,
+ &reason_code) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for "
+ MACSTR, reason_code, MAC2STR(sta->addr));
+ sta->disconnect_reason_code = reason_code;
+ }
break;
case RADIUS_CODE_ACCESS_CHALLENGE:
sm->eap_if->aaaEapReq = TRUE;
@@ -1837,6 +1946,19 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
if (override_eapReq)
sm->eap_if->aaaEapReq = FALSE;
+#ifdef CONFIG_FILS
+#ifdef NEED_AP_MLME
+ if (sta->flags & WLAN_STA_PENDING_FILS_ERP) {
+ /* TODO: Add a PMKSA entry on success? */
+ ieee802_11_finish_fils_auth(
+ hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
+ sm->eap_if->aaaEapReqData,
+ sm->eap_if->aaaEapKeyData,
+ sm->eap_if->aaaEapKeyDataLen);
+ }
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_FILS */
+
eapol_auth_step(sm);
return RADIUS_RX_QUEUED;
@@ -1924,7 +2046,7 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
eapol->default_wep_key_idx);
-
+
if (ieee802_1x_rekey_broadcast(hapd)) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_WARNING, "failed to generate a "
@@ -2034,13 +2156,19 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
}
if (eap_user->password) {
- user->password = os_malloc(eap_user->password_len);
+ user->password = os_memdup(eap_user->password,
+ eap_user->password_len);
if (user->password == NULL)
goto out;
- os_memcpy(user->password, eap_user->password,
- eap_user->password_len);
user->password_len = eap_user->password_len;
user->password_hash = eap_user->password_hash;
+ if (eap_user->salt && eap_user->salt_len) {
+ user->salt = os_memdup(eap_user->salt,
+ eap_user->salt_len);
+ if (!user->salt)
+ goto out;
+ user->salt_len = eap_user->salt_len;
+ }
}
user->force_version = eap_user->force_version;
user->macacl = eap_user->macacl;
@@ -2190,6 +2318,7 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.erp_domain = hapd->conf->erp_domain;
conf.erp = hapd->conf->eap_server_erp;
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ conf.tls_flags = hapd->conf->tls_flags;
conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
@@ -2326,6 +2455,16 @@ int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
MAC2STR(sta->addr), xhdr->version, xhdr->type,
be_to_host16(xhdr->length), ack);
+#ifdef CONFIG_WPS
+ if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
+ (sta->flags & WLAN_STA_WPS) &&
+ ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Indicate EAP completion on ACK for EAP-Failure");
+ hostapd_wps_eap_completed(hapd);
+ }
+#endif /* CONFIG_WPS */
+
if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
return 0;
@@ -2642,6 +2781,15 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
sta->hs20_deauth_req);
}
+
+ if (sta->hs20_t_c_filtering) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+ MACSTR " to indicate Terms and Conditions filtering",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url);
+ os_free(sta->t_c_url);
+ sta->t_c_url = NULL;
+ }
}
#endif /* CONFIG_HS20 */
@@ -2655,6 +2803,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
/* TODO: get PMKLifetime from WPA parameters */
static const int dot11RSNAConfigPMKLifetime = 43200;
unsigned int session_timeout;
+ struct os_reltime now, remaining;
#ifdef CONFIG_HS20
if (remediation && !sta->remediation) {
@@ -2665,7 +2814,8 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
sta->remediation_method = 1; /* SOAP-XML SPP */
}
- if (success && (sta->remediation || sta->hs20_deauth_req)) {
+ if (success && (sta->remediation || sta->hs20_deauth_req ||
+ sta->hs20_t_c_filtering)) {
wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
MACSTR " in 100 ms", MAC2STR(sta->addr));
eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
@@ -2675,10 +2825,13 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
key = ieee802_1x_get_key(sta->eapol_sm, &len);
- if (sta->session_timeout_set)
- session_timeout = sta->session_timeout;
- else
+ if (sta->session_timeout_set) {
+ os_get_reltime(&now);
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+ session_timeout = (remaining.sec > 0) ? remaining.sec : 1;
+ } else {
session_timeout = dot11RSNAConfigPMKLifetime;
+ }
if (success && key && len >= PMK_LEN && !sta->remediation &&
!sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
@@ -2699,15 +2852,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
* EAP-FAST with anonymous provisioning, may require another
* EAPOL authentication to be started to complete connection.
*/
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
- "disconnection after EAP-Failure");
- /* Add a small sleep to increase likelihood of previously
- * requested EAP-Failure TX getting out before this should the
- * driver reorder operations.
- */
- os_sleep(0, 10000);
- ap_sta_disconnect(hapd, sta, sta->addr,
- WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
- hostapd_wps_eap_completed(hapd);
+ ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta);
}
}
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index ec80199007b6..9594661be8ee 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -57,5 +57,10 @@ int add_common_radius_attr(struct hostapd_data *hapd,
struct hostapd_radius_attr *req_attr,
struct sta_info *sta,
struct radius_msg *msg);
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *eap, size_t len);
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* IEEE802_1X_H */
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 3c086bfc7131..4d6a92e086bd 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -182,4 +182,5 @@ int ndisc_snoop_init(struct hostapd_data *hapd)
void ndisc_snoop_deinit(struct hostapd_data *hapd)
{
l2_packet_deinit(hapd->sock_ndisc);
+ hapd->sock_ndisc = NULL;
}
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index a2efff618286..b8fd5924b889 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -43,6 +43,7 @@ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
nr->civic = NULL;
os_memset(nr->bssid, 0, sizeof(nr->bssid));
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+ nr->stationary = 0;
}
@@ -64,7 +65,7 @@ hostapd_neighbor_add(struct hostapd_data *hapd)
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)
+ const struct wpabuf *civic, int stationary)
{
struct hostapd_neighbor_entry *entry;
@@ -83,18 +84,20 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
if (!entry->nr)
goto fail;
- if (lci) {
+ if (lci && wpabuf_len(lci)) {
entry->lci = wpabuf_dup(lci);
if (!entry->lci || os_get_time(&entry->lci_date))
goto fail;
}
- if (civic) {
+ if (civic && wpabuf_len(civic)) {
entry->civic = wpabuf_dup(civic);
if (!entry->civic)
goto fail;
}
+ entry->stationary = stationary;
+
return 0;
fail:
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index c22e043c120e..ba46d88435e5 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -16,7 +16,7 @@ hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
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);
+ const struct wpabuf *civic, int stationary);
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);
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
deleted file mode 100644
index efc1d7e4c78f..000000000000
--- a/src/ap/peerkey_auth.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * hostapd - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2009, Jouni Malinen
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "utils/includes.h"
-
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "crypto/sha1.h"
-#include "crypto/sha256.h"
-#include "crypto/random.h"
-#include "wpa_auth.h"
-#include "wpa_auth_i.h"
-#include "wpa_auth_ie.h"
-
-#ifdef CONFIG_PEERKEY
-
-static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx)
-{
-#if 0
- struct wpa_authenticator *wpa_auth = eloop_ctx;
- struct wpa_stsl_negotiation *neg = timeout_ctx;
-#endif
-
- /* TODO: ? */
-}
-
-
-struct wpa_stsl_search {
- const u8 *addr;
- struct wpa_state_machine *sm;
-};
-
-
-static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx)
-{
- struct wpa_stsl_search *search = ctx;
- if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) {
- search->sm = sm;
- return 1;
- }
- return 0;
-}
-
-
-static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, const u8 *peer,
- u16 mui, u16 error_type)
-{
- u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)];
- u8 *pos;
- struct rsn_error_kde error;
-
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
- "Sending SMK Error");
-
- pos = kde;
-
- if (peer) {
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN,
- NULL, 0);
- }
-
- error.mui = host_to_be16(mui);
- error.error_type = host_to_be16(error_type);
- pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR,
- (u8 *) &error, sizeof(error), NULL, 0);
-
- __wpa_send_eapol(wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR,
- NULL, NULL, kde, pos - kde, 0, 0, 0);
-}
-
-
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct wpa_stsl_search search;
- u8 *buf, *pos;
- size_t buf_len;
-
- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
- return;
- }
-
- if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
- kde.mac_addr_len < ETH_ALEN) {
- wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
- "SMK M1");
- return;
- }
-
- /* Initiator = sm->addr; Peer = kde.mac_addr */
-
- search.addr = kde.mac_addr;
- search.sm = NULL;
- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
- 0 || search.sm == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
- " aborted - STA not associated anymore",
- MAC2STR(kde.mac_addr));
- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
- STK_ERR_STA_NR);
- /* FIX: wpa_stsl_remove(wpa_auth, neg); */
- return;
- }
-
- buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return;
- /* Initiator RSN IE */
- os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len);
- pos = buf + kde.rsn_ie_len;
- /* Initiator MAC Address */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN,
- NULL, 0);
-
- /* SMK M2:
- * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
- * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE)
- */
-
- wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG,
- "Sending SMK M2");
-
- __wpa_send_eapol(wpa_auth, search.sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE,
- NULL, key->key_nonce, buf, pos - buf, 0, 0, 0);
-
- os_free(buf);
-}
-
-
-static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- struct wpa_eapol_key *key,
- struct wpa_eapol_ie_parse *kde,
- const u8 *smk)
-{
- u8 *buf, *pos;
- size_t buf_len;
- u32 lifetime;
-
- /* SMK M4:
- * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce,
- * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE,
- * Lifetime KDE)
- */
-
- buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
- pos = buf = os_malloc(buf_len);
- if (buf == NULL)
- return;
-
- /* Initiator MAC Address */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN,
- NULL, 0);
-
- /* Initiator Nonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN,
- NULL, 0);
-
- /* SMK with PNonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
- key->key_nonce, WPA_NONCE_LEN);
-
- /* Lifetime */
- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
- (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "Sending SMK M4");
-
- __wpa_send_eapol(wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE,
- NULL, key->key_nonce, buf, pos - buf, 0, 1, 0);
-
- os_free(buf);
-}
-
-
-static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- struct wpa_eapol_key *key,
- struct wpa_eapol_ie_parse *kde,
- const u8 *smk, const u8 *peer)
-{
- u8 *buf, *pos;
- size_t buf_len;
- u32 lifetime;
-
- /* SMK M5:
- * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
- * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE,
- * Lifetime KDE))
- */
-
- buf_len = kde->rsn_ie_len +
- 2 + RSN_SELECTOR_LEN + ETH_ALEN +
- 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN +
- 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
- pos = buf = os_malloc(buf_len);
- if (buf == NULL)
- return;
-
- /* Peer RSN IE */
- os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
- pos += kde->rsn_ie_len;
-
- /* Peer MAC Address */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
-
- /* PNonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce,
- WPA_NONCE_LEN, NULL, 0);
-
- /* SMK and INonce */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN,
- kde->nonce, WPA_NONCE_LEN);
-
- /* Lifetime */
- lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */
- pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
- (u8 *) &lifetime, sizeof(lifetime), NULL, 0);
-
- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "Sending SMK M5");
-
- __wpa_send_eapol(wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
- WPA_KEY_INFO_SMK_MESSAGE,
- NULL, kde->nonce, buf, pos - buf, 0, 1, 0);
-
- os_free(buf);
-}
-
-
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct wpa_stsl_search search;
- u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
-
- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
- return;
- }
-
- if (kde.rsn_ie == NULL ||
- kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
- kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) {
- wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or "
- "Nonce KDE in SMK M3");
- return;
- }
-
- /* Peer = sm->addr; Initiator = kde.mac_addr;
- * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */
-
- search.addr = kde.mac_addr;
- search.sm = NULL;
- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
- 0 || search.sm == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR
- " aborted - STA not associated anymore",
- MAC2STR(kde.mac_addr));
- wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
- STK_ERR_STA_NR);
- /* FIX: wpa_stsl_remove(wpa_auth, neg); */
- return;
- }
-
- if (random_get_bytes(smk, PMK_LEN)) {
- wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
- return;
- }
-
- /* SMK = PRF-256(Random number, "SMK Derivation",
- * AA || Time || INonce || PNonce)
- */
- os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
- pos = buf + ETH_ALEN;
- wpa_get_ntp_timestamp(pos);
- pos += 8;
- os_memcpy(pos, kde.nonce, WPA_NONCE_LEN);
- pos += WPA_NONCE_LEN;
- os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN);
-#ifdef CONFIG_IEEE80211W
- sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
- smk, PMK_LEN);
-#else /* CONFIG_IEEE80211W */
- sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf),
- smk, PMK_LEN);
-#endif /* CONFIG_IEEE80211W */
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN);
-
- wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk);
- wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr);
-
- /* Authenticator does not need SMK anymore and it is required to forget
- * it. */
- os_memset(smk, 0, sizeof(*smk));
-}
-
-
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- const u8 *key_data, size_t key_data_len)
-{
- struct wpa_eapol_ie_parse kde;
- struct wpa_stsl_search search;
- struct rsn_error_kde error;
- u16 mui, error_type;
-
- if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
- return;
- }
-
- if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
- kde.error == NULL || kde.error_len < sizeof(error)) {
- wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in "
- "SMK Error");
- return;
- }
-
- search.addr = kde.mac_addr;
- search.sm = NULL;
- if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) ==
- 0 || search.sm == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not "
- "associated for SMK Error message from " MACSTR,
- MAC2STR(kde.mac_addr), MAC2STR(sm->addr));
- return;
- }
-
- os_memcpy(&error, kde.error, sizeof(error));
- mui = be_to_host16(error.mui);
- error_type = be_to_host16(error.error_type);
- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
- "STA reported SMK Error: Peer " MACSTR
- " MUI %d Error Type %d",
- MAC2STR(kde.mac_addr), mui, error_type);
-
- wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
-}
-
-
-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
- struct wpa_stsl_negotiation *neg)
-{
- struct wpa_stsl_negotiation *pos, *prev;
-
- if (wpa_auth == NULL)
- return -1;
- pos = wpa_auth->stsl_negotiations;
- prev = NULL;
- while (pos) {
- if (pos == neg) {
- if (prev)
- prev->next = pos->next;
- else
- wpa_auth->stsl_negotiations = pos->next;
-
- eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos);
- os_free(pos);
- return 0;
- }
- prev = pos;
- pos = pos->next;
- }
-
- return -1;
-}
-
-#endif /* CONFIG_PEERKEY */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index d610e7e5b005..15e2c4943f2b 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -282,7 +282,42 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp)
{
- struct rsn_pmksa_cache_entry *entry, *pos;
+ struct rsn_pmksa_cache_entry *entry;
+
+ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
+ aa, spa, session_timeout, eapol,
+ akmp);
+
+ if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
+ return NULL;
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function creates a PMKSA entry.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
+{
+ struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
if (pmk_len > PMK_LEN_MAX)
@@ -303,8 +338,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
else if (wpa_key_mgmt_suite_b(akmp))
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
else
- rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
- wpa_key_mgmt_sha256(akmp));
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
entry->expiration = now.sec;
if (session_timeout > 0)
@@ -315,9 +349,30 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
os_memcpy(entry->spa, spa, ETH_ALEN);
pmksa_cache_from_eapol_data(entry, eapol);
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @entry: Pointer to PMKSA cache entry
+ *
+ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
+ * already in the cache for the same Supplicant, this entry will be replaced
+ * with the new entry. PMKID will be calculated based on the PMK.
+ */
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ struct rsn_pmksa_cache_entry *pos;
+
+ if (entry == NULL)
+ return -1;
+
/* Replace an old entry for the same STA (if found) with the new entry
*/
- pos = pmksa_cache_auth_get(pmksa, spa, NULL);
+ pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
if (pos)
pmksa_cache_free_entry(pmksa, pos);
@@ -331,7 +386,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
pmksa_cache_link_entry(pmksa, entry);
- return entry;
+ return 0;
}
@@ -462,7 +517,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
continue;
rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
- wpa_key_mgmt_sha256(entry->akmp));
+ entry->akmp);
if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
@@ -605,3 +660,70 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
}
return pos - buf;
}
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+/**
+ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @addr: MAC address of the peer (NULL means any)
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
+ * in external storage.
+ */
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+ char *buf, size_t len)
+{
+ int ret;
+ char *pos, *end;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ pos = buf;
+ end = buf + len;
+ os_get_reltime(&now);
+
+
+ /*
+ * Entry format:
+ *
+ */
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+ continue;
+
+ ret = os_snprintf(pos, end - pos, MACSTR " ",
+ MAC2STR(entry->spa));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
+ PMKID_LEN);
+
+ ret = os_snprintf(pos, end - pos, " ");
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
+ entry->pmk_len);
+
+ ret = os_snprintf(pos, end - pos, " %d\n",
+ (int) (entry->expiration - now.sec));
+ if (os_snprintf_error(end - pos, ret))
+ return 0;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index d8d9c5a25c0e..2ef217435b11 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -35,6 +35,7 @@ struct rsn_pmksa_cache_entry {
};
struct rsn_pmksa_cache;
+struct radius_das_attrs;
struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
@@ -53,6 +54,13 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ const u8 *kck, size_t kck_len, const u8 *aa,
+ const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp);
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa, const u8 *pmkid);
@@ -65,5 +73,7 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
struct radius_das_attrs *attr);
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+ char *buf, size_t len);
#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index 3569f955bcd2..56ed29c1c068 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -2,6 +2,7 @@
* hostapd / Radio Measurement (RRM)
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ * Copyright (c) 2016-2017, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +11,7 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_drv_ops.h"
#include "sta_info.h"
@@ -69,24 +71,47 @@ static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
}
+static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+ const u8 *addr, u8 token, u8 rep_mode,
+ const u8 *pos, size_t len)
+{
+ char report[2 * 255 + 1];
+
+ wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
+ token, len, MAC2STR(addr));
+ /* Skip to the beginning of the Beacon report */
+ if (len < 3)
+ return;
+ pos += 3;
+ len -= 3;
+ report[0] = '\0';
+ if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+ MAC2STR(addr), token, rep_mode, report);
+}
+
+
static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
const u8 *buf, size_t len)
{
const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
const u8 *pos, *ie, *end;
- u8 token;
+ u8 token, rep_mode;
end = buf + len;
token = mgmt->u.action.u.rrm.dialog_token;
pos = mgmt->u.action.u.rrm.variable;
while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
- if (ie[1] < 5) {
+ if (ie[1] < 3) {
wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
break;
}
- wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]);
+ rep_mode = ie[3];
+ wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
+ rep_mode, ie[4]);
switch (ie[4]) {
case MEASURE_TYPE_LCI:
@@ -95,6 +120,10 @@ static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
case MEASURE_TYPE_FTM_RANGE:
hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
break;
+ case MEASURE_TYPE_BEACON:
+ hostapd_handle_beacon_report(hapd, mgmt->sa, token,
+ rep_mode, ie + 2, ie[1]);
+ break;
default:
wpa_printf(MSG_DEBUG,
"Measurement report type %u is not supported",
@@ -118,7 +147,7 @@ static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
/* Subelements are arranged as IEs */
subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
if (subelem && subelem[1] == 2)
- return *(u16 *) (subelem + 2);
+ return WPA_GET_LE16(subelem + 2);
return 0;
}
@@ -129,12 +158,12 @@ static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
struct os_time curr, diff;
unsigned long diff_l;
+ if (nr->stationary || max_age == 0xffff)
+ return 1;
+
if (!max_age)
return 0;
- if (max_age == 0xffff)
- return 1;
-
if (os_get_time(&curr))
return 0;
@@ -341,13 +370,7 @@ int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
struct sta_info *sta = ap_get_sta(hapd, addr);
int ret;
- if (!sta) {
- wpa_printf(MSG_INFO,
- "Request LCI: Destination address is not in station list");
- return -1;
- }
-
- if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
wpa_printf(MSG_INFO,
"Request LCI: Destination address is not connected");
return -1;
@@ -450,9 +473,8 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
wpa_printf(MSG_DEBUG,
"Request range: Range request is already in process; overriding");
hapd->range_req_active = 0;
- eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
- hostapd_range_rep_timeout_handler, hapd,
- NULL);
+ eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
+ NULL);
}
/* Action + measurement type + token + reps + EID + len = 7 */
@@ -542,3 +564,111 @@ void hostapd_clean_rrm(struct hostapd_data *hapd)
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
hapd->range_req_active = 0;
}
+
+
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+ u8 req_mode, const struct wpabuf *req)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ int ret;
+ enum beacon_report_mode mode;
+ const u8 *pos;
+
+ /* Request data:
+ * Operating Class (1), Channel Number (1), Randomization Interval (2),
+ * Measurement Duration (2), Measurement Mode (1), BSSID (6),
+ * Optional Subelements (variable)
+ */
+ if (wpabuf_len(req) < 13) {
+ wpa_printf(MSG_INFO, "Beacon request: Too short request data");
+ return -1;
+ }
+ pos = wpabuf_head(req);
+ mode = pos[6];
+
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR " is not connected",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ switch (mode) {
+ case BEACON_REPORT_MODE_PASSIVE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support passive beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ case BEACON_REPORT_MODE_ACTIVE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support active beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ case BEACON_REPORT_MODE_TABLE:
+ if (!(sta->rrm_enabled_capa[0] &
+ WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
+ wpa_printf(MSG_INFO,
+ "Beacon request: " MACSTR
+ " does not support table beacon report",
+ MAC2STR(addr));
+ return -1;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "Beacon request: Unknown measurement mode %d", mode);
+ return -1;
+ }
+
+ buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
+ if (!buf)
+ return -1;
+
+ hapd->beacon_req_token++;
+ if (!hapd->beacon_req_token)
+ hapd->beacon_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->beacon_req_token);
+ wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+ /* Measurement Request element */
+ wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+ wpabuf_put_u8(buf, 3 + wpabuf_len(req));
+ wpabuf_put_u8(buf, 1); /* Measurement Token */
+ wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
+ wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
+ wpabuf_put_buf(buf, req);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret < 0)
+ return ret;
+
+ return hapd->beacon_req_token;
+}
+
+
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ if (len < 24 + 3)
+ return;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
+ " %u ack=%d", MAC2STR(mgmt->da),
+ mgmt->u.action.u.rrm.dialog_token, ok);
+}
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
index f07fd41ac019..02cd522ee9d6 100644
--- a/src/ap/rrm.h
+++ b/src/ap/rrm.h
@@ -24,5 +24,10 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
u16 random_interval, u8 min_ap,
const u8 *responders, unsigned int n_responders);
void hostapd_clean_rrm(struct hostapd_data *hapd);
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+ u8 req_mode, const struct wpabuf *req);
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok);
#endif /* RRM_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index f12d4088b131..179cf43b60b1 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2013, Jouni Malinen
+ * Copyright (c) 2002-2017, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -17,6 +17,7 @@
#include "radius/radius_client.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
+#include "crypto/crypto.h"
#include "hostapd.h"
#include "accounting.h"
#include "ieee802_1x.h"
@@ -36,6 +37,7 @@
#include "ndisc_snoop.h"
#include "sta_info.h"
#include "vlan.h"
+#include "wps_hostapd.h"
static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta);
@@ -47,6 +49,7 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
#endif /* CONFIG_IEEE80211W */
static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
@@ -194,7 +197,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 0;
hapd->iface->num_sta_no_short_slot_time--;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_slot_time == 0)
set_beacon++;
}
@@ -202,7 +206,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->no_short_preamble_set) {
sta->no_short_preamble_set = 0;
hapd->iface->num_sta_no_short_preamble--;
- if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 0)
set_beacon++;
}
@@ -316,6 +321,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
wpabuf_free(sta->wps_ie);
wpabuf_free(sta->p2p_ie);
wpabuf_free(sta->hs20_ie);
+ wpabuf_free(sta->roaming_consortium);
#ifdef CONFIG_FST
wpabuf_free(sta->mb_ies);
#endif /* CONFIG_FST */
@@ -326,6 +332,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->identity);
os_free(sta->radius_cui);
os_free(sta->remediation_url);
+ os_free(sta->t_c_url);
wpabuf_free(sta->hs20_deauth_req);
os_free(sta->hs20_session_info_url);
@@ -337,6 +344,31 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
mbo_ap_sta_free(sta);
os_free(sta->supp_op_classes);
+#ifdef CONFIG_FILS
+ os_free(sta->fils_pending_assoc_req);
+ wpabuf_free(sta->fils_hlp_resp);
+ wpabuf_free(sta->hlp_dhcp_discover);
+ eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+ crypto_ecdh_deinit(sta->fils_ecdh);
+ wpabuf_clear_free(sta->fils_dh_ss);
+ wpabuf_free(sta->fils_g_sta);
+#endif /* CONFIG_FILS_SK_PFS */
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ bin_clear_free(sta->owe_pmk, sta->owe_pmk_len);
+ crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
+ os_free(sta->ext_capability);
+
+#ifdef CONFIG_WNM_AP
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+#endif /* CONFIG_WNM_AP */
+
+ os_free(sta->ifname_wds);
+
os_free(sta);
}
@@ -597,7 +629,7 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
{
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
@@ -608,7 +640,7 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
sta->hs20_disassoc_timer);
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
}
@@ -745,9 +777,17 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
- sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Skip deauthentication in DMG/IEEE 802.11ad */
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_REMOVE;
+ } else {
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_DEAUTH;
+ }
ap_sta_set_authorized(hapd, sta, 0);
- sta->timeout_next = STA_DEAUTH;
wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
"for " MACSTR " (%d seconds - "
"AP_MAX_INACTIVITY_AFTER_DISASSOC)",
@@ -783,6 +823,14 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ ap_sta_disassociate(hapd, sta, reason);
+ return;
+ }
+
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
@@ -1229,6 +1277,20 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
ap_handle_timer, hapd, sta);
sta->timeout_next = STA_REMOVE;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Deauthentication is not used in DMG/IEEE 802.11ad;
+ * disassociate the STA instead. */
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
+ 2 : 0, 0, ap_sta_disassoc_cb_timeout,
+ hapd, sta);
+ return;
+ }
+
sta->deauth_reason = reason;
sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -1275,6 +1337,15 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
"%s: Removed ap_sta_disassoc_cb_timeout timeout for "
MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
+ if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0)
+ {
+ wpa_printf(MSG_DEBUG,
+ "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for "
+ MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ if (sta->flags & WLAN_STA_WPS)
+ hostapd_wps_eap_completed(hapd);
+ }
}
@@ -1283,7 +1354,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
int res;
buf[0] = '\0';
- res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1300,6 +1371,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
(flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
(flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
(flags & WLAN_STA_GAS ? "[GAS]" : ""),
+ (flags & WLAN_STA_HT ? "[HT]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
@@ -1309,3 +1381,48 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
return res;
}
+
+
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+ u16 reason;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X: Scheduled disconnection of " MACSTR
+ " after EAP-Failure", MAC2STR(sta->addr));
+
+ reason = sta->disconnect_reason_code;
+ if (!reason)
+ reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED;
+ ap_sta_disconnect(hapd, sta, sta->addr, reason);
+ if (sta->flags & WLAN_STA_WPS)
+ hostapd_wps_eap_completed(hapd);
+}
+
+
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X: Force disconnection of " MACSTR
+ " after EAP-Failure in 10 ms", MAC2STR(sta->addr));
+
+ /*
+ * Add a small sleep to increase likelihood of previously requested
+ * EAP-Failure TX getting out before this should the driver reorder
+ * operations.
+ */
+ eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+ eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb,
+ hapd, sta);
+}
+
+
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb,
+ hapd, sta);
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 099de62d1a9a..9cac6f157f68 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen
+ * Copyright (c) 2002-2017, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,14 +9,11 @@
#ifndef STA_INFO_H
#define STA_INFO_H
-#ifdef CONFIG_MESH
-/* needed for mesh_plink_state enum */
#include "common/defs.h"
-#include "common/wpa_common.h"
-#endif /* CONFIG_MESH */
-
#include "list.h"
#include "vlan.h"
+#include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -38,6 +35,7 @@
#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT(22)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -46,6 +44,7 @@
* Supported Rates IEs). */
#define WLAN_SUPP_RATES_MAX 32
+struct hostapd_data;
struct mbo_non_pref_chan_info {
struct mbo_non_pref_chan_info *next;
@@ -68,6 +67,7 @@ struct sta_info {
be32 ipaddr;
struct dl_list ip6addr; /* list head for struct ip6addr */
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
+ u16 disconnect_reason_code; /* RADIUS server override */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;
u16 listen_interval; /* or beacon_int for APs */
@@ -113,6 +113,10 @@ struct sta_info {
unsigned int radius_das_match:1;
unsigned int ecsa_supported:1;
unsigned int added_unassoc:1;
+ unsigned int pending_wds_enable:1;
+ unsigned int power_capab:1;
+ unsigned int agreed_to_steer:1;
+ unsigned int hs20_t_c_filtering:1;
u16 auth_alg;
@@ -170,17 +174,20 @@ struct sta_info {
struct os_reltime sa_query_start;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_INTERWORKING
+#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
struct gas_dialog_info *gas_dialog;
u8 gas_dialog_next;
-#endif /* CONFIG_INTERWORKING */
+#endif /* CONFIG_INTERWORKING || CONFIG_DPP */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+ /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */
+ struct wpabuf *roaming_consortium;
u8 remediation_method;
char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */
struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url;
int hs20_disassoc_timer;
@@ -195,7 +202,8 @@ struct sta_info {
unsigned int mesh_sae_pmksa_caching:1;
#endif /* CONFIG_SAE */
- u32 session_timeout; /* valid only if session_timeout_set == 1 */
+ /* valid only if session_timeout_set == 1 */
+ struct os_reltime session_timeout;
/* Last Authentication/(Re)Association Request/Action frame sequence
* control */
@@ -214,10 +222,51 @@ struct sta_info {
u8 rrm_enabled_capa[5];
+ s8 min_tx_power;
+ s8 max_tx_power;
+
#ifdef CONFIG_TAXONOMY
struct wpabuf *probe_ie_taxonomy;
struct wpabuf *assoc_ie_taxonomy;
#endif /* CONFIG_TAXONOMY */
+
+#ifdef CONFIG_FILS
+ u8 fils_snonce[FILS_NONCE_LEN];
+ u8 fils_session[FILS_SESSION_LEN];
+ u8 fils_erp_pmkid[PMKID_LEN];
+ u8 *fils_pending_assoc_req;
+ size_t fils_pending_assoc_req_len;
+ unsigned int fils_pending_assoc_is_reassoc:1;
+ unsigned int fils_dhcp_rapid_commit_proxy:1;
+ unsigned int fils_erp_pmkid_set:1;
+ unsigned int fils_drv_assoc_finish:1;
+ struct wpabuf *fils_hlp_resp;
+ struct wpabuf *hlp_dhcp_discover;
+ void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 resp, struct wpabuf *data, int pub);
+#ifdef CONFIG_FILS_SK_PFS
+ struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+ struct wpabuf *fils_dh_ss;
+ struct wpabuf *fils_g_sta;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ u8 *owe_pmk;
+ size_t owe_pmk_len;
+ struct crypto_ecdh *owe_ecdh;
+ u16 owe_group;
+#endif /* CONFIG_OWE */
+
+ u8 *ext_capability;
+ char *ifname_wds; /* WDS ifname, if in use */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ enum wpa_alg last_tk_alg;
+ int last_tk_key_idx;
+ u8 last_tk[WPA_TK_MAX_LEN];
+ size_t last_tk_len;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -237,8 +286,6 @@ struct sta_info {
#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
-struct hostapd_data;
-
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
void *ctx),
@@ -289,5 +336,9 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
struct sta_info *sta);
int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta);
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta);
#endif /* STA_INFO_H */
diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c
index cea8b726f47a..ae157a7c91d6 100644
--- a/src/ap/taxonomy.c
+++ b/src/ap/taxonomy.c
@@ -21,6 +21,7 @@
#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "sta_info.h"
+#include "taxonomy.h"
/* Copy a string with no funny schtuff allowed; only alphanumerics. */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4725e2b3e8f1..557570cd1bc4 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -71,6 +71,11 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
struct os_reltime now;
int ret = 0;
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in received frame%s",
+ local ? " (local)" : "");
+
if (addr && local) {
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta != NULL) {
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 31e4fc6b396a..01fecee026a1 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -138,6 +138,8 @@ int vlan_init(struct hostapd_data *hapd)
!hapd->conf->vlan) {
/* dynamic vlans enabled but no (or empty) vlan_file given */
struct hostapd_vlan *vlan;
+ int ret;
+
vlan = os_zalloc(sizeof(*vlan));
if (vlan == NULL) {
wpa_printf(MSG_ERROR, "Out of memory while assigning "
@@ -146,8 +148,16 @@ int vlan_init(struct hostapd_data *hapd)
}
vlan->vlan_id = VLAN_ID_WILDCARD;
- os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
- hapd->conf->iface);
+ ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+ hapd->conf->iface);
+ if (ret >= (int) sizeof(vlan->ifname)) {
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan->ifname);
+ } else if (ret < 0) {
+ os_free(vlan);
+ return ret;
+ }
vlan->next = hapd->conf->vlan;
hapd->conf->vlan = vlan;
}
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 314e244bc956..8054c5d2f243 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -21,11 +21,6 @@
#include "wmm.h"
-/* TODO: maintain separate sequence and fragment numbers for each AC
- * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
- * if only WMM stations are receiving a certain group */
-
-
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
{
u8 ret;
@@ -157,8 +152,9 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
int wmm_process_tspec(struct wmm_tspec_element *tspec)
{
- int medium_time, pps, duration;
- int up, psb, dir, tid;
+ u64 medium_time;
+ unsigned int pps, duration;
+ unsigned int up, psb, dir, tid;
u16 val, surplus;
up = (tspec->ts_info[1] >> 3) & 0x07;
@@ -206,8 +202,9 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec)
return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
}
- medium_time = surplus * pps * duration / 0x2000;
- wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+ medium_time = (u64) surplus * pps * duration / 0x2000;
+ wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
+ (unsigned long) medium_time);
/*
* TODO: store list of granted (and still active) TSPECs and check
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 41d50cebfbe0..1e8f58b4d07e 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -95,8 +95,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Response action frame");
- os_free(wnmtfs_ie);
- return -1;
+ res = -1;
+ goto fail;
}
os_memcpy(mgmt->da, addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
@@ -109,6 +109,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
/* add key data if MFP is enabled */
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys ||
action_type != WNM_SLEEP_MODE_EXIT) {
mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
} else {
@@ -118,11 +119,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
(int) gtk_elem_len);
#ifdef CONFIG_IEEE80211W
res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
- if (res < 0) {
- os_free(wnmtfs_ie);
- os_free(mgmt);
- return -1;
- }
+ if (res < 0)
+ goto fail;
igtk_elem_len = res;
pos += igtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
@@ -176,7 +174,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
wpa_set_wnmsleep(sta->wpa_sm, 0);
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
addr, NULL, NULL);
- if (!wpa_auth_uses_mfp(sta->wpa_sm))
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ hapd->conf->wnm_sleep_mode_no_keys)
wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
}
} else
@@ -184,6 +183,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#undef MAX_GTK_SUBELEM_LEN
#undef MAX_IGTK_SUBELEM_LEN
+fail:
os_free(wnmtfs_ie);
os_free(mgmt);
return res;
@@ -202,12 +202,20 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0;
+ if (!hapd->conf->wnm_sleep_mode) {
+ wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
+ MACSTR " since WNM-Sleep Mode is disabled",
+ MAC2STR(addr));
+ return;
+ }
+
dialog_token = *pos++;
while (pos + 1 < frm + len) {
u8 ie_len = pos[1];
if (pos + 2 + ie_len > frm + len)
break;
- if (*pos == WLAN_EID_WNMSLEEP)
+ if (*pos == WLAN_EID_WNMSLEEP &&
+ ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
wnmsleep_ie = (struct wnm_sleep_element *) pos;
else if (*pos == WLAN_EID_TFS_REQ) {
if (!tfsreq_ie_start)
@@ -251,20 +259,14 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
const u8 *addr,
- u8 dialog_token,
- const char *url)
+ u8 dialog_token)
{
struct ieee80211_mgmt *mgmt;
- size_t url_len, len;
+ size_t len;
u8 *pos;
int res;
- if (url)
- url_len = os_strlen(url);
- else
- url_len = 0;
-
- mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
+ mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL)
return -1;
os_memcpy(mgmt->da, addr, ETH_ALEN);
@@ -279,11 +281,6 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
mgmt->u.action.u.bss_tm_req.validity_interval = 1;
pos = mgmt->u.action.u.bss_tm_req.variable;
- if (url) {
- *pos++ += url_len;
- os_memcpy(pos, url, url_len);
- pos += url_len;
- }
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
@@ -307,6 +304,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
{
u8 dialog_token, reason;
const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Query from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
if (len < 2) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
@@ -326,7 +337,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
pos, end - pos);
- ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
+ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
+}
+
+
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ if (sta->agreed_to_steer) {
+ wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+ sta->agreed_to_steer = 0;
+ }
}
@@ -336,6 +360,21 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
{
u8 dialog_token, status_code, bss_termination_delay;
const u8 *pos, *end;
+ int enabled = hapd->conf->bss_transition;
+ struct sta_info *sta;
+
+#ifdef CONFIG_MBO
+ if (hapd->conf->mbo_enabled)
+ enabled = 1;
+#endif /* CONFIG_MBO */
+ if (!enabled) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore BSS Transition Management Response from "
+ MACSTR
+ " since BSS Transition Management is disabled",
+ MAC2STR(addr));
+ return;
+ }
if (len < 3) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
@@ -354,11 +393,23 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
"bss_termination_delay=%u", MAC2STR(addr), dialog_token,
status_code, bss_termination_delay);
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for received BSS TM Response",
+ MAC2STR(addr));
+ return;
+ }
+
if (status_code == WNM_BSS_TM_ACCEPT) {
if (end - pos < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
return;
}
+ sta->agreed_to_steer = 1;
+ eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
+ eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
+ hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
MAC2STR(pos));
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
@@ -368,6 +419,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
MAC2STR(pos));
pos += ETH_ALEN;
} else {
+ sta->agreed_to_steer = 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
" status_code=%u bss_termination_delay=%u",
MAC2STR(addr), status_code, bss_termination_delay);
@@ -401,6 +453,48 @@ static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
}
+static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *buf,
+ size_t len)
+{
+ u8 dialog_token;
+ char *hex;
+ size_t hex_len;
+
+ if (!hapd->conf->coloc_intf_reporting) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore unexpected Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short Collocated Interference Report from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+ dialog_token = *buf++;
+ len--;
+
+ wpa_printf(MSG_DEBUG,
+ "WNM: Received Collocated Interference Report frame from "
+ MACSTR " (dialog_token=%u)",
+ MAC2STR(addr), dialog_token);
+ wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
+ buf, len);
+
+ hex_len = 2 * len + 1;
+ hex = os_malloc(hex_len);
+ if (!hex)
+ return;
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
+ MAC2STR(addr), dialog_token, hex);
+ os_free(hex);
+}
+
+
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -431,6 +525,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
plen);
return 0;
+ case WNM_COLLOCATED_INTERFERENCE_REPORT:
+ ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
}
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
@@ -629,3 +727,40 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
return 0;
}
+
+
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout)
+{
+ u8 buf[100], *pos;
+ struct ieee80211_mgmt *mgmt;
+ u8 dialog_token = 1;
+
+ if (auto_report > 3 || timeout > 63)
+ return -1;
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.coloc_intf_req.action =
+ WNM_COLLOCATED_INTERFERENCE_REQ;
+ mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
+ mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
+ pos = &mgmt->u.action.u.coloc_intf_req.req_info;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
+ MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
+ MAC2STR(sta->addr), dialog_token, auto_report, timeout);
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Failed to send Collocated Interference Request frame");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index a44eadb85e55..1806ba0e0525 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -23,5 +23,8 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *bss_term_dur, const char *url,
const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len);
+void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
+int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
+ unsigned int auto_report, unsigned int timeout);
#endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index bf10cc1646f7..34969e79e46f 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-2015, Jouni Malinen
+ * Copyright (c) 2004-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,10 +13,13 @@
#include "utils/state_machine.h"
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap_config.h"
@@ -33,8 +36,14 @@
static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
- size_t data_len);
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+ u8 *data, size_t data_len);
+#ifdef CONFIG_FILS
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *buf, size_t buf_len, u16 *_key_data_len);
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+ const struct wpabuf *hlp);
+#endif /* CONFIG_FILS */
static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
@@ -52,12 +61,12 @@ static void wpa_group_get(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
-static const u32 dot11RSNAConfigGroupUpdateCount = 4;
-static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
static const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
static const u32 eapol_key_timeout_first_group = 500; /* ms */
+static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
/* TODO: make these configurable */
static const int dot11RSNAConfigPMKLifetime = 43200;
@@ -68,8 +77,8 @@ static const int dot11RSNAConfigSATimeout = 60;
static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- if (wpa_auth->cb.mic_failure_report)
- return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+ if (wpa_auth->cb->mic_failure_report)
+ return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
return 0;
}
@@ -77,8 +86,8 @@ static inline int wpa_auth_mic_failure_report(
static inline void wpa_auth_psk_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- if (wpa_auth->cb.psk_failure_report)
- wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
+ if (wpa_auth->cb->psk_failure_report)
+ wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
}
@@ -86,38 +95,38 @@ static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
const u8 *addr, wpa_eapol_variable var,
int value)
{
- if (wpa_auth->cb.set_eapol)
- wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
+ if (wpa_auth->cb->set_eapol)
+ wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
}
static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
const u8 *addr, wpa_eapol_variable var)
{
- if (wpa_auth->cb.get_eapol == NULL)
+ if (wpa_auth->cb->get_eapol == NULL)
return -1;
- return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
+ return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
}
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)
+ const u8 *prev_psk, size_t *psk_len)
{
- if (wpa_auth->cb.get_psk == NULL)
+ 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);
+ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+ prev_psk, psk_len);
}
static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
const u8 *addr, u8 *msk, size_t *len)
{
- if (wpa_auth->cb.get_msk == NULL)
+ if (wpa_auth->cb->get_msk == NULL)
return -1;
- return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
+ return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
}
@@ -126,19 +135,19 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
enum wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len)
{
- if (wpa_auth->cb.set_key == NULL)
+ if (wpa_auth->cb->set_key == NULL)
return -1;
- return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
- key, key_len);
+ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+ key, key_len);
}
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
- if (wpa_auth->cb.get_seqnum == NULL)
+ if (wpa_auth->cb->get_seqnum == NULL)
return -1;
- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
}
@@ -146,10 +155,10 @@ static inline int
wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *data, size_t data_len, int encrypt)
{
- if (wpa_auth->cb.send_eapol == NULL)
+ if (wpa_auth->cb->send_eapol == NULL)
return -1;
- return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
- encrypt);
+ return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
+ encrypt);
}
@@ -157,9 +166,9 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
const u8 *addr)
{
- if (wpa_auth->cb.start_ampe == NULL)
+ if (wpa_auth->cb->start_ampe == NULL)
return -1;
- return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+ return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
}
#endif /* CONFIG_MESH */
@@ -168,9 +177,9 @@ int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx)
{
- if (wpa_auth->cb.for_each_sta == NULL)
+ if (wpa_auth->cb->for_each_sta == NULL)
return 0;
- return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
+ return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
}
@@ -178,18 +187,18 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx)
{
- if (wpa_auth->cb.for_each_auth == NULL)
+ if (wpa_auth->cb->for_each_auth == NULL)
return 0;
- return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+ return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx);
}
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt)
{
- if (wpa_auth->cb.logger == NULL)
+ if (wpa_auth->cb->logger == NULL)
return;
- wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
+ wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
}
@@ -200,7 +209,7 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
int maxlen;
va_list ap;
- if (wpa_auth->cb.logger == NULL)
+ if (wpa_auth->cb->logger == NULL)
return;
maxlen = os_strlen(fmt) + 100;
@@ -219,30 +228,13 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
- const u8 *addr)
+ const u8 *addr, u16 reason)
{
- if (wpa_auth->cb.disconnect == NULL)
+ if (wpa_auth->cb->disconnect == NULL)
return;
- wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
- wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-}
-
-
-static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
-{
- int ret = 0;
-#ifdef CONFIG_IEEE80211R
- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
- ret = 1;
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
- if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
- ret = 1;
-#endif /* CONFIG_IEEE80211W */
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
- ret = 1;
- return ret;
+ wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
+ MAC2STR(addr), reason);
+ wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
}
@@ -409,7 +401,8 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
*/
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
- struct wpa_auth_callbacks *cb)
+ const struct wpa_auth_callbacks *cb,
+ void *cb_ctx)
{
struct wpa_authenticator *wpa_auth;
@@ -418,7 +411,8 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
- os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
+ wpa_auth->cb = cb;
+ wpa_auth->cb_ctx = cb_ctx;
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
@@ -443,7 +437,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (wpa_auth->ft_pmk_cache == NULL) {
wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
@@ -453,7 +447,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
os_free(wpa_auth);
return NULL;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (wpa_auth->conf.wpa_gmk_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
@@ -506,17 +500,13 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
-#ifdef CONFIG_PEERKEY
- while (wpa_auth->stsl_negotiations)
- wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
-#endif /* CONFIG_PEERKEY */
-
pmksa_cache_auth_deinit(wpa_auth->pmksa);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
wpa_auth->ft_pmk_cache = NULL;
-#endif /* CONFIG_IEEE80211R */
+ wpa_ft_deinit(wpa_auth);
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
bitfield_free(wpa_auth->ip_pool);
@@ -599,16 +589,28 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return -1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (sm->ft_completed) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"FT authentication already completed - do not "
"start 4-way handshake");
/* Go to PTKINITDONE state to allow GTK rekeying */
sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = TRUE;
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+ if (sm->fils_completed) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "FILS authentication already completed - do not start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+ sm->Pair = TRUE;
+ return 0;
+ }
+#endif /* CONFIG_FILS */
if (sm->started) {
os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
@@ -660,10 +662,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
os_free(sm->assoc_resp_ftie);
wpabuf_free(sm->ft_pending_req_ies);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
wpa_group_put(sm->wpa_auth, sm->group);
@@ -680,15 +682,19 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"strict rekeying - force GTK rekey since STA "
"is leaving");
- eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
- eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
- NULL);
+ if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
+ sm->wpa_auth, NULL) == -1)
+ eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
+ NULL);
}
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+#ifdef CONFIG_IEEE80211R_AP
+ wpa_ft_sta_deinit(sm);
+#endif /* CONFIG_IEEE80211R_AP */
if (sm->in_step_loop) {
/* Must not free state machine while wpa_sm_step() is running.
* Freeing will be completed in the end of wpa_sm_step(). */
@@ -739,7 +745,7 @@ static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde)
@@ -786,7 +792,7 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
@@ -828,29 +834,38 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
struct wpa_ptk PTK;
int ok = 0;
const u8 *pmk = NULL;
- unsigned int pmk_len;
+ size_t pmk_len;
+ os_memset(&PTK, 0, sizeof(PTK));
for (;;) {
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ 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);
+ sm->p2p_dev_addr, pmk, &pmk_len);
if (pmk == NULL)
break;
- pmk_len = PMK_LEN;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
- wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
+ if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
+ break;
- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
- == 0) {
+ if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
+ data, data_len) == 0) {
ok = 1;
break;
}
- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
@@ -877,39 +892,41 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
u16 key_info, key_data_length;
- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
- SMK_M1, SMK_M3, SMK_ERROR } msg;
+ enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
char *msgtxt;
struct wpa_eapol_ie_parse kde;
- int ft;
- const u8 *eapol_key_ie, *key_data;
- size_t eapol_key_ie_len, keyhdrlen, mic_len;
+ const u8 *key_data;
+ size_t keyhdrlen, mic_len;
+ u8 *mic;
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return;
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
- mic_len = wpa_mic_len(sm->wpa_key_mgmt);
- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
- if (data_len < sizeof(*hdr) + keyhdrlen)
+ if (data_len < sizeof(*hdr) + keyhdrlen) {
+ wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
return;
+ }
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ mic = (u8 *) (key + 1);
key_info = WPA_GET_BE16(key->key_info);
- if (mic_len == 24) {
- key_data = (const u8 *) (key192 + 1);
- key_data_length = WPA_GET_BE16(key192->key_data_length);
- } else {
- key_data = (const u8 *) (key + 1);
- key_data_length = WPA_GET_BE16(key->key_data_length);
- }
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
- " key_info=0x%x type=%u key_data_length=%u",
- MAC2STR(sm->addr), key_info, key->type, key_data_length);
+ " key_info=0x%x type=%u mic_len=%u key_data_length=%u",
+ MAC2STR(sm->addr), key_info, key->type,
+ (unsigned int) mic_len, key_data_length);
+ wpa_hexdump(MSG_MSGDUMP,
+ "WPA: EAPOL-Key header (ending before Key MIC)",
+ key, sizeof(*key));
+ wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+ mic, mic_len);
if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
"key_data overflow (%d > %lu)",
@@ -950,25 +967,20 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
* are set */
- if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
- (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
- if (key_info & WPA_KEY_INFO_ERROR) {
- msg = SMK_ERROR;
- msgtxt = "SMK Error";
- } else {
- msg = SMK_M1;
- msgtxt = "SMK M1";
- }
- } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
- msg = SMK_M3;
- msgtxt = "SMK M3";
- } else if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
+ return;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
msg = GROUP_2;
msgtxt = "2/2 Group";
- } else if (key_data_length == 0) {
+ } else if (key_data_length == 0 ||
+ (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ key_data_length == AES_BLOCK_SIZE)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
@@ -976,15 +988,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
msgtxt = "2/4 Pairwise";
}
- /* TODO: key_info type validation for PeerKey */
if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
msg == GROUP_2) {
u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (sm->pairwise == WPA_CIPHER_CCMP ||
sm->pairwise == WPA_CIPHER_GCMP) {
- if (wpa_use_aes_cmac(sm) &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
- !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+ if (wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -994,7 +1004,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
return;
}
- if (!wpa_use_aes_cmac(sm) &&
+ if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -1004,7 +1015,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
}
- if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1092,6 +1103,15 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
continue_processing:
+#ifdef CONFIG_FILS
+ if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
+ !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
+ return;
+ }
+#endif /* CONFIG_FILS */
+
switch (msg) {
case PAIRWISE_2:
if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
@@ -1119,70 +1139,10 @@ continue_processing:
"collect more entropy for random number "
"generation");
random_mark_pool_ready();
- wpa_sta_disconnect(wpa_auth, sm->addr);
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
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 msg 2/4 with "
- "invalid Key Data contents");
- return;
- }
- if (kde.rsn_ie) {
- eapol_key_ie = kde.rsn_ie;
- eapol_key_ie_len = kde.rsn_ie_len;
- } else if (kde.osen) {
- eapol_key_ie = kde.osen;
- eapol_key_ie_len = kde.osen_len;
- } else {
- eapol_key_ie = kde.wpa_ie;
- eapol_key_ie_len = kde.wpa_ie_len;
- }
- ft = sm->wpa == WPA_VERSION_WPA2 &&
- wpa_key_mgmt_ft(sm->wpa_key_mgmt);
- if (sm->wpa_ie == NULL ||
- wpa_compare_rsn_ie(ft,
- sm->wpa_ie, sm->wpa_ie_len,
- eapol_key_ie, eapol_key_ie_len)) {
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "WPA IE from (Re)AssocReq did not "
- "match with msg 2/4");
- if (sm->wpa_ie) {
- wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
- sm->wpa_ie, sm->wpa_ie_len);
- }
- wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
- eapol_key_ie, eapol_key_ie_len);
- /* MLME-DEAUTHENTICATE.request */
- wpa_sta_disconnect(wpa_auth, sm->addr);
- return;
- }
-#ifdef CONFIG_IEEE80211R
- if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
- wpa_sta_disconnect(wpa_auth, sm->addr);
- return;
- }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_P2P
- if (kde.ip_addr_req && kde.ip_addr_req[0] &&
- wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
- int idx;
- wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
- "EAPOL-Key exchange");
- idx = bitfield_get_first_zero(wpa_auth->ip_pool);
- if (idx >= 0) {
- u32 start = WPA_GET_BE32(wpa_auth->conf.
- ip_addr_start);
- bitfield_set(wpa_auth->ip_pool, idx);
- WPA_PUT_BE32(sm->ip_addr, start + idx);
- wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
- "address %u.%u.%u.%u to " MACSTR,
- sm->ip_addr[0], sm->ip_addr[1],
- sm->ip_addr[2], sm->ip_addr[3],
- MAC2STR(sm->addr));
- }
- }
-#endif /* CONFIG_P2P */
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1204,28 +1164,6 @@ continue_processing:
return;
}
break;
-#ifdef CONFIG_PEERKEY
- case SMK_M1:
- case SMK_M3:
- case SMK_ERROR:
- if (!wpa_auth->conf.peerkey) {
- wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
- "PeerKey use disabled - ignoring message");
- return;
- }
- if (!sm->PTK_valid) {
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "received EAPOL-Key msg SMK in "
- "invalid state - dropped");
- return;
- }
- break;
-#else /* CONFIG_PEERKEY */
- case SMK_M1:
- case SMK_M3:
- case SMK_ERROR:
- return; /* STSL disabled - ignore SMK messages */
-#endif /* CONFIG_PEERKEY */
case REQUEST:
break;
}
@@ -1239,22 +1177,42 @@ continue_processing:
return;
}
- if (!(key_info & WPA_KEY_INFO_MIC)) {
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ !(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC not set");
return;
}
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ (key_info & WPA_KEY_INFO_MIC)) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received invalid EAPOL-Key: Key MIC set");
+ return;
+ }
+#endif /* CONFIG_FILS */
+
sm->MICVerified = FALSE;
if (sm->PTK_valid && !sm->update_snonce) {
- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
- data_len) &&
+ if (mic_len &&
+ wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
+ data, data_len) &&
(msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
wpa_try_alt_snonce(sm, data, data_len))) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
return;
}
+#ifdef CONFIG_FILS
+ if (!mic_len &&
+ wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+ &key_data_length) < 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key with invalid MIC");
+ return;
+ }
+#endif /* CONFIG_FILS */
sm->MICVerified = TRUE;
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
@@ -1277,12 +1235,7 @@ continue_processing:
* even though MAC address KDE is not normally encrypted,
* supplicant is allowed to encrypt it.
*/
- if (msg == SMK_ERROR) {
-#ifdef CONFIG_PEERKEY
- wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
-#endif /* CONFIG_PEERKEY */
- return;
- } else if (key_info & WPA_KEY_INFO_ERROR) {
+ if (key_info & WPA_KEY_INFO_ERROR) {
if (wpa_receive_error_report(
wpa_auth, sm,
!(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
@@ -1292,11 +1245,6 @@ continue_processing:
"received EAPOL-Key Request for new "
"4-Way Handshake");
wpa_request_new_ptk(sm);
-#ifdef CONFIG_PEERKEY
- } else if (msg == SMK_M1) {
- wpa_smk_m1(wpa_auth, sm, key, key_data,
- key_data_length);
-#endif /* CONFIG_PEERKEY */
} else if (key_data_length > 0 &&
wpa_parse_kde_ies(key_data, key_data_length,
&kde) == 0 &&
@@ -1335,18 +1283,10 @@ continue_processing:
wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
}
-#ifdef CONFIG_PEERKEY
- if (msg == SMK_M3) {
- wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
- return;
- }
-#endif /* CONFIG_PEERKEY */
-
os_free(sm->last_rx_eapol_key);
- sm->last_rx_eapol_key = os_malloc(data_len);
+ sm->last_rx_eapol_key = os_memdup(data, data_len);
if (sm->last_rx_eapol_key == NULL)
return;
- os_memcpy(sm->last_rx_eapol_key, data, data_len);
sm->last_rx_eapol_key_len = data_len;
sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1361,7 +1301,7 @@ continue_processing:
static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
- u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
u8 *pos;
int ret = 0;
@@ -1372,21 +1312,30 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
* is done only at the Authenticator and as such, does not need to be
* exactly same.
*/
+ os_memset(data, 0, sizeof(data));
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
pos = data + ETH_ALEN + WPA_NONCE_LEN;
wpa_get_ntp_timestamp(pos);
pos += 8;
- if (random_get_bytes(pos, 16) < 0)
+ if (random_get_bytes(pos, gtk_len) < 0)
ret = -1;
-#ifdef CONFIG_IEEE80211W
- sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
-#else /* CONFIG_IEEE80211W */
- if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
- < 0)
+#ifdef CONFIG_SHA384
+ if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
ret = -1;
-#endif /* CONFIG_IEEE80211W */
+#else /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA256
+ if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#else /* CONFIG_SHA256 */
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
+ gtk, gtk_len) < 0)
+ ret = -1;
+#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
return ret;
}
@@ -1412,26 +1361,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
size_t len, mic_len, keyhdrlen;
int alg;
int key_data_len, pad_len = 0;
u8 *buf, *pos;
int version, pairwise;
int i;
- u8 *key_data;
+ u8 *key_mic, *key_data;
- mic_len = wpa_mic_len(sm->wpa_key_mgmt);
- keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ keyhdrlen = sizeof(*key) + mic_len + 2;
len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
if (force_version)
version = force_version;
- else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+ else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
- else if (wpa_use_aes_cmac(sm))
+ else if (wpa_use_cmac(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
@@ -1453,8 +1400,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@@ -1463,6 +1409,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
}
len += key_data_len;
+ if (!mic_len && encr)
+ len += AES_BLOCK_SIZE;
hdr = os_zalloc(len);
if (hdr == NULL)
@@ -1471,7 +1419,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
hdr->length = host_to_be16(len - sizeof(*hdr));
key = (struct wpa_eapol_key *) (hdr + 1);
- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ key_mic = (u8 *) (key + 1);
key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
key->type = sm->wpa == WPA_VERSION_WPA2 ?
@@ -1484,11 +1432,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
WPA_PUT_BE16(key->key_info, key_info);
alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
- WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
- if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+ if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
WPA_PUT_BE16(key->key_length, 0);
+ else
+ WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
- /* FIX: STSL: what to use as key_replay_counter? */
for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
os_memcpy(sm->key_replay[i].counter,
@@ -1510,10 +1458,31 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (kde && !encr) {
os_memcpy(key_data, kde, kde_len);
- if (mic_len == 24)
- WPA_PUT_BE16(key192->key_data_length, kde_len);
- else
- WPA_PUT_BE16(key->key_data_length, kde_len);
+ WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+ } else if (!mic_len && kde) {
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+ wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+ kde, kde_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+ sm->PTK.kek, sm->PTK.kek_len);
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = (u8 *) hdr;
+ aad_len[0] = key_mic + 2 - (u8 *) hdr;
+ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+ 1, aad, aad_len, key_mic + 2) < 0) {
+ wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+ key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
} else if (encr && kde) {
buf = os_zalloc(key_data_len);
if (buf == NULL) {
@@ -1530,24 +1499,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
- wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)",
+ (unsigned int) sm->PTK.kek_len);
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
(key_data_len - 8) / 8, buf, key_data)) {
os_free(hdr);
os_free(buf);
return;
}
- if (mic_len == 24)
- WPA_PUT_BE16(key192->key_data_length,
- key_data_len);
- else
- WPA_PUT_BE16(key->key_data_length,
- key_data_len);
+ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
#ifndef CONFIG_NO_RC4
} else if (sm->PTK.kek_len == 16) {
u8 ek[32];
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Encrypt Key Data using RC4");
os_memcpy(key->key_iv,
sm->group->Counter + WPA_NONCE_LEN - 16, 16);
inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
@@ -1555,12 +1524,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
os_memcpy(key_data, buf, key_data_len);
rc4_skip(ek, 32, 256, key_data, key_data_len);
- if (mic_len == 24)
- WPA_PUT_BE16(key192->key_data_length,
- key_data_len);
- else
- WPA_PUT_BE16(key->key_data_length,
- key_data_len);
+ WPA_PUT_BE16(key_mic + mic_len, key_data_len);
#endif /* CONFIG_NO_RC4 */
} else {
os_free(hdr);
@@ -1571,9 +1535,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
}
if (key_info & WPA_KEY_INFO_MIC) {
- u8 *key_mic;
-
- if (!sm->PTK_valid) {
+ if (!sm->PTK_valid || !mic_len) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PTK not valid when sending EAPOL-Key "
"frame");
@@ -1581,10 +1543,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
return;
}
- key_mic = key192->key_mic; /* same offset for key and key192 */
- wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
- sm->wpa_key_mgmt, version,
- (u8 *) hdr, len, key_mic);
+ if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+ sm->wpa_key_mgmt, version,
+ (u8 *) hdr, len, key_mic) < 0) {
+ os_free(hdr);
+ return;
+ }
#ifdef CONFIG_TESTING_OPTIONS
if (!pairwise &&
wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
@@ -1613,7 +1577,7 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
{
int timeout_ms;
int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
- int ctr;
+ u32 ctr;
if (sm == NULL)
return;
@@ -1627,41 +1591,43 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
eapol_key_timeout_first_group;
else
timeout_ms = eapol_key_timeout_subseq;
+ if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
+ timeout_ms = eapol_key_timeout_no_retrans;
if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
sm->pending_1_of_4_timeout = 1;
wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
- "counter %d)", timeout_ms, ctr);
+ "counter %u)", timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
-static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
- size_t data_len)
+static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
+ u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- struct wpa_eapol_key_192 *key192;
u16 key_info;
int ret = 0;
- u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
- size_t mic_len = wpa_mic_len(akmp);
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
+ size_t mic_len = wpa_mic_len(akmp, pmk_len);
if (data_len < sizeof(*hdr) + sizeof(*key))
return -1;
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
- key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ mic_pos = (u8 *) (key + 1);
key_info = WPA_GET_BE16(key->key_info);
- os_memcpy(mic, key192->key_mic, mic_len);
- os_memset(key192->key_mic, 0, mic_len);
+ os_memcpy(mic, mic_pos, mic_len);
+ os_memset(mic_pos, 0, mic_len);
if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
key_info & WPA_KEY_INFO_TYPE_MASK,
- data, data_len, key192->key_mic) ||
- os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+ data, data_len, mic_pos) ||
+ os_memcmp_const(mic, mic_pos, mic_len) != 0)
ret = -1;
- os_memcpy(key192->key_mic, mic, mic_len);
+ os_memcpy(mic_pos, mic, mic_len);
return ret;
}
@@ -1670,7 +1636,10 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
{
sm->PTK_valid = FALSE;
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
- wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+ if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+ 0))
+ wpa_printf(MSG_DEBUG,
+ "RSN: PTK removal from the driver failed");
sm->pairwise_set = FALSE;
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
@@ -1734,7 +1703,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
sm->ReAuthenticationRequest = TRUE;
break;
case WPA_ASSOC_FT:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
"after association");
wpa_ft_install_ptk(sm);
@@ -1742,22 +1711,37 @@ 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;
return 0;
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
break;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+ case WPA_ASSOC_FILS:
+#ifdef CONFIG_FILS
+ wpa_printf(MSG_DEBUG,
+ "FILS: TK configuration after association");
+ fils_set_tk(sm);
+ sm->fils_completed = 1;
+ return 0;
+#else /* CONFIG_FILS */
+ break;
+#endif /* CONFIG_FILS */
case WPA_DRV_STA_REMOVED:
sm->tk_already_set = FALSE;
return 0;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
sm->ft_completed = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (sm->mgmt_frame_prot && event == WPA_AUTH)
remove_ptk = 0;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_FILS
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+ (event == WPA_AUTH || event == WPA_ASSOC))
+ remove_ptk = 0;
+#endif /* CONFIG_FILS */
if (remove_ptk) {
sm->PTK_valid = FALSE;
@@ -1802,7 +1786,9 @@ SM_STATE(WPA_PTK, INITIALIZE)
wpa_remove_ptk(sm);
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
sm->TimeoutCtr = 0;
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 0);
}
@@ -1811,9 +1797,14 @@ SM_STATE(WPA_PTK, INITIALIZE)
SM_STATE(WPA_PTK, DISCONNECT)
{
+ u16 reason = sm->disconnect_reason;
+
SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
sm->Disconnect = FALSE;
- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ sm->disconnect_reason = 0;
+ if (!reason)
+ reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
}
@@ -1922,17 +1913,25 @@ SM_STATE(WPA_PTK, INITPMK)
size_t len = 2 * PMK_LEN;
SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
sm->xxkey_len = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
+#ifdef CONFIG_DPP
+ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PMKSA cache entry for STA - reject connection");
+ sm->Disconnect = TRUE;
+ sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
+ return;
+#endif /* CONFIG_DPP */
} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
unsigned int pmk_len;
- if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
@@ -1948,15 +1947,20 @@ SM_STATE(WPA_PTK, INITPMK)
}
os_memcpy(sm->PMK, msk, pmk_len);
sm->pmk_len = pmk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (len >= 2 * PMK_LEN) {
- os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
- sm->xxkey_len = PMK_LEN;
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
+ sm->xxkey_len = SHA384_MAC_LEN;
+ } else {
+ os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ }
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
} else {
wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
- sm->wpa_auth->cb.get_msk);
+ sm->wpa_auth->cb->get_msk);
sm->Disconnect = TRUE;
return;
}
@@ -1978,16 +1982,26 @@ SM_STATE(WPA_PTK, INITPMK)
SM_STATE(WPA_PTK, INITPSK)
{
const u8 *psk;
+ size_t psk_len;
+
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
- psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
+ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
+ &psk_len);
if (psk) {
- os_memcpy(sm->PMK, psk, PMK_LEN);
- sm->pmk_len = PMK_LEN;
-#ifdef CONFIG_IEEE80211R
+ os_memcpy(sm->PMK, psk, psk_len);
+ sm->pmk_len = psk_len;
+#ifdef CONFIG_IEEE80211R_AP
os_memcpy(sm->xxkey, psk, PMK_LEN);
sm->xxkey_len = PMK_LEN;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
}
+#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+ 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;
+ }
+#endif /* CONFIG_SAE */
sm->req_replay_counter_used = 0;
}
@@ -2003,7 +2017,7 @@ SM_STATE(WPA_PTK, PTKSTART)
sm->alt_snonce_valid = FALSE;
sm->TimeoutCtr++;
- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2012,11 +2026,23 @@ SM_STATE(WPA_PTK, PTKSTART)
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
- * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
- * one possible PSK for this STA.
+ * For infrastructure BSS cases, it is better for the AP not to include
+ * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate
+ * offline search for the passphrase/PSK without having to be able to
+ * capture a 4-way handshake from a STA that has access to the network.
+ *
+ * For IBSS cases, addition of PMKID KDE could be considered even with
+ * WPA2-PSK cases that use multiple PSKs, but only if there is a single
+ * possible PSK for this STA. However, this should not be done unless
+ * there is support for using that information on the supplicant side.
+ * The concern about exposing PMKID unnecessarily in infrastructure BSS
+ * cases would also apply here, but at least in the IBSS case, this
+ * would cover a potential real use case.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
- wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+ (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
+ (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
@@ -2024,11 +2050,31 @@ SM_STATE(WPA_PTK, PTKSTART)
pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
if (sm->pmksa) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from PMKSA entry",
+ sm->pmksa->pmkid, PMKID_LEN);
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmksa->pmkid, PMKID_LEN);
} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
/* No KCK available to derive PMKID */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No KCK available to derive PMKID for message 1/4");
pmkid = NULL;
+#ifdef CONFIG_SAE
+ } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
+ if (sm->pmkid_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from SAE",
+ 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 SAE PMKID available for message 1/4");
+ pmkid = NULL;
+ }
+#endif /* CONFIG_SAE */
} else {
/*
* Calculate PMKID since no PMKSA cache entry was
@@ -2036,7 +2082,10 @@ SM_STATE(WPA_PTK, PTKSTART)
*/
rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
- wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
+ sm->wpa_key_mgmt);
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID derived from PMK",
+ &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
}
}
wpa_send_eapol(sm->wpa_auth, sm,
@@ -2049,10 +2098,10 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk)
{
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
@@ -2060,43 +2109,587 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
}
+#ifdef CONFIG_FILS
+
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *snonce, const u8 *anonce,
+ const u8 *dhss, size_t dhss_len,
+ struct wpabuf *g_sta, struct wpabuf *g_ap)
+{
+ u8 ick[FILS_ICK_MAX_LEN];
+ size_t ick_len;
+ int res;
+ u8 fils_ft[FILS_FT_MAX_LEN];
+ size_t fils_ft_len = 0;
+
+ res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
+ snonce, anonce, dhss, dhss_len,
+ &sm->PTK, ick, &ick_len,
+ sm->wpa_key_mgmt, sm->pairwise,
+ fils_ft, &fils_ft_len);
+ if (res < 0)
+ return res;
+ sm->PTK_valid = TRUE;
+ sm->tk_already_set = FALSE;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (fils_ft_len) {
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+ size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+
+ if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
+ conf->ssid, conf->ssid_len,
+ conf->mobility_domain,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ sm->addr, pmk_r0, pmk_r0_name,
+ use_sha384) < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+ pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+ 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));
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+ sm->addr, sm->wpa_auth->addr,
+ g_sta ? wpabuf_head(g_sta) : NULL,
+ g_sta ? wpabuf_len(g_sta) : 0,
+ g_ap ? wpabuf_head(g_ap) : NULL,
+ g_ap ? wpabuf_len(g_ap) : 0,
+ sm->wpa_key_mgmt, sm->fils_key_auth_sta,
+ sm->fils_key_auth_ap,
+ &sm->fils_key_auth_len);
+ os_memset(ick, 0, sizeof(ick));
+
+ /* Store nonces for (Re)Association Request/Response frame processing */
+ os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+ os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+ return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+ u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u8 *pos;
+ u16 key_data_len;
+ u8 *tmp;
+ const u8 *aad[1];
+ size_t aad_len[1];
+
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ pos = (u8 *) (key + 1);
+ key_data_len = WPA_GET_BE16(pos);
+ if (key_data_len < AES_BLOCK_SIZE ||
+ key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "No room for AES-SIV data in the frame");
+ return -1;
+ }
+ pos += 2; /* Pointing at the Encrypted Key Data field */
+
+ tmp = os_malloc(key_data_len);
+ if (!tmp)
+ return -1;
+
+ /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+ * to Key Data (exclusive). */
+ aad[0] = buf;
+ aad_len[0] = pos - buf;
+ if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
+ 1, aad, aad_len, tmp) < 0) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "Invalid AES-SIV data in the frame");
+ bin_clear_free(tmp, key_data_len);
+ return -1;
+ }
+
+ /* AEAD decryption and validation completed successfully */
+ key_data_len -= AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+ tmp, key_data_len);
+
+ /* Replace Key Data field with the decrypted version */
+ os_memcpy(pos, tmp, key_data_len);
+ pos -= 2; /* Key Data Length field */
+ WPA_PUT_BE16(pos, key_data_len);
+ bin_clear_free(tmp, key_data_len);
+ if (_key_data_len)
+ *_key_data_len = key_data_len;
+ return 0;
+}
+
+
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *fils_session)
+{
+ const u8 *ie, *end;
+ const u8 *session = NULL;
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not a FILS AKM - reject association");
+ return NULL;
+ }
+
+ /* Verify Session element */
+ ie = ies;
+ end = ((const u8 *) ie) + ies_len;
+ while (ie + 1 < end) {
+ if (ie + 2 + ie[1] > end)
+ break;
+ if (ie[0] == WLAN_EID_EXTENSION &&
+ ie[1] >= 1 + FILS_SESSION_LEN &&
+ ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+ session = ie;
+ break;
+ }
+ ie += 2 + ie[1];
+ }
+
+ if (!session) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: %s: Could not find FILS Session element in Assoc Req - reject",
+ __func__);
+ return NULL;
+ }
+
+ if (!fils_session) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: %s: Could not find FILS Session element in STA entry - reject",
+ __func__);
+ return NULL;
+ }
+
+ if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+ fils_session, FILS_SESSION_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+ session + 3, FILS_SESSION_LEN);
+ return NULL;
+ }
+ return session;
+}
+
+
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Failed to parse decrypted elements");
+ return -1;
+ }
+
+ if (!elems.fils_session) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+ return -1;
+ }
+
+ if (!elems.fils_key_confirm) {
+ wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+ return -1;
+ }
+
+ if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Unexpected Key-Auth length %d (expected %d)",
+ elems.fils_key_confirm_len,
+ (int) sm->fils_key_auth_len);
+ return -1;
+ }
+
+ if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+ sm->fils_key_auth_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+ wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+ elems.fils_key_confirm, elems.fils_key_confirm_len);
+ wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+ sm->fils_key_auth_sta, sm->fils_key_auth_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+ const struct ieee80211_mgmt *mgmt, size_t frame_len,
+ u8 *pos, size_t left)
+{
+ u16 fc, stype;
+ const u8 *end, *ie_start, *ie, *session, *crypt;
+ const u8 *aad[5];
+ size_t aad_len[5];
+
+ if (!sm || !sm->PTK_valid) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: No KEK to decrypt Assocication Request frame");
+ return -1;
+ }
+
+ if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not a FILS AKM - reject association");
+ return -1;
+ }
+
+ end = ((const u8 *) mgmt) + frame_len;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+ ie_start = mgmt->u.reassoc_req.variable;
+ else
+ ie_start = mgmt->u.assoc_req.variable;
+ ie = ie_start;
+
+ /*
+ * Find FILS Session element which is the last unencrypted element in
+ * the frame.
+ */
+ session = wpa_fils_validate_fils_session(sm, ie, end - ie,
+ fils_session);
+ if (!session) {
+ wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
+ return -1;
+ }
+
+ crypt = session + 2 + session[1];
+
+ if (end - crypt < AES_BLOCK_SIZE) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Too short frame to include AES-SIV data");
+ return -1;
+ }
+
+ /* AES-SIV AAD vectors */
+
+ /* The STA's MAC address */
+ aad[0] = mgmt->sa;
+ aad_len[0] = ETH_ALEN;
+ /* The AP's BSSID */
+ aad[1] = mgmt->da;
+ aad_len[1] = ETH_ALEN;
+ /* The STA's nonce */
+ aad[2] = sm->SNonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The AP's nonce */
+ aad[3] = sm->ANonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Request frame from the Capability Information
+ * field to the FILS Session element (both inclusive).
+ */
+ aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+ aad_len[4] = crypt - aad[4];
+
+ if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+ 5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Invalid AES-SIV data in the frame");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+ pos, left - AES_BLOCK_SIZE);
+
+ if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
+ wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
+ return -1;
+ }
+
+ return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+ size_t current_len, size_t max_len,
+ const struct wpabuf *hlp)
+{
+ u8 *end = buf + max_len;
+ u8 *pos = buf + current_len;
+ struct ieee80211_mgmt *mgmt;
+ struct wpabuf *plain;
+ const u8 *aad[5];
+ size_t aad_len[5];
+
+ if (!sm || !sm->PTK_valid)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: Association Response frame before FILS processing",
+ buf, current_len);
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+
+ /* AES-SIV AAD vectors */
+
+ /* The AP's BSSID */
+ aad[0] = mgmt->sa;
+ aad_len[0] = ETH_ALEN;
+ /* The STA's MAC address */
+ aad[1] = mgmt->da;
+ aad_len[1] = ETH_ALEN;
+ /* The AP's nonce */
+ aad[2] = sm->ANonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The STA's nonce */
+ aad[3] = sm->SNonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Response frame from the Capability Information
+ * field (the same offset in both Association and Reassociation
+ * Response frames) to the FILS Session element (both inclusive).
+ */
+ aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+ aad_len[4] = pos - aad[4];
+
+ /* The following elements will be encrypted with AES-SIV */
+ plain = fils_prepare_plainbuf(sm, hlp);
+ if (!plain) {
+ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+ return -1;
+ }
+
+ if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Not enough room for FILS elements");
+ wpabuf_free(plain);
+ return -1;
+ }
+
+ wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+ plain);
+
+ if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+ wpabuf_head(plain), wpabuf_len(plain),
+ 5, aad, aad_len, pos) < 0) {
+ wpabuf_free(plain);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "FILS: Encrypted Association Response elements",
+ pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+ current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+ wpabuf_free(plain);
+
+ sm->fils_completed = 1;
+
+ return current_len;
+}
+
+
+static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
+ const struct wpabuf *hlp)
+{
+ struct wpabuf *plain;
+ u8 *len, *tmp, *tmp2;
+ u8 hdr[2];
+ u8 *gtk, dummy_gtk[32];
+ size_t gtk_len;
+ struct wpa_group *gsm;
+
+ plain = wpabuf_alloc(1000);
+ if (!plain)
+ return NULL;
+
+ /* TODO: FILS Public Key */
+
+ /* FILS Key Confirmation */
+ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+ wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+ /* Element ID Extension */
+ wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+ /* FILS HLP Container */
+ if (hlp)
+ wpabuf_put_buf(plain, hlp);
+
+ /* TODO: FILS IP Address Assignment */
+
+ /* Key Delivery */
+ gsm = sm->group;
+ wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+ len = wpabuf_put(plain, 1);
+ wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+ wpabuf_put(plain, WPA_KEY_RSC_LEN));
+ /* GTK KDE */
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+ gtk = dummy_gtk;
+ }
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ wpabuf_put(plain, tmp2 - tmp);
+
+ /* IGTK KDE */
+ tmp = wpabuf_put(plain, 0);
+ tmp2 = ieee80211w_kde_add(sm, tmp);
+ wpabuf_put(plain, tmp2 - tmp);
+
+ *len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+ return plain;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+ enum wpa_alg alg;
+ int klen;
+
+ if (!sm || !sm->PTK_valid) {
+ wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
+ return -1;
+ }
+ if (sm->tk_already_set) {
+ wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
+ return -1;
+ }
+
+ alg = wpa_cipher_to_alg(sm->pairwise);
+ klen = wpa_cipher_key_len(sm->pairwise);
+
+ wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+ if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+ sm->PTK.tk, klen)) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+ return -1;
+ }
+ sm->tk_already_set = TRUE;
+
+ return 0;
+}
+
+
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
+ const u8 *fils_session, struct wpabuf *hlp)
+{
+ struct wpabuf *plain;
+ u8 *pos = buf;
+
+ /* FILS Session */
+ *pos++ = WLAN_EID_EXTENSION; /* Element ID */
+ *pos++ = 1 + FILS_SESSION_LEN; /* Length */
+ *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+ os_memcpy(pos, fils_session, FILS_SESSION_LEN);
+ pos += FILS_SESSION_LEN;
+
+ plain = fils_prepare_plainbuf(sm, hlp);
+ if (!plain) {
+ wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
+ return NULL;
+ }
+
+ os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
+ pos += wpabuf_len(plain);
+
+ wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__,
+ (unsigned int) wpabuf_len(plain));
+ wpabuf_free(plain);
+ sm->fils_completed = 1;
+ return pos;
+}
+
+#endif /* CONFIG_FILS */
+
+
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_ptk PTK;
int ok = 0, psk_found = 0;
const u8 *pmk = NULL;
- unsigned int pmk_len;
+ size_t pmk_len;
+ int ft;
+ const u8 *eapol_key_ie, *key_data, *mic;
+ u16 key_data_length;
+ size_t mic_len, eapol_key_ie_len;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
sm->update_snonce = FALSE;
+ os_memset(&PTK, 0, sizeof(PTK));
+
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
for (;;) {
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ 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);
+ sm->p2p_dev_addr, pmk, &pmk_len);
if (pmk == NULL)
break;
psk_found = 1;
- pmk_len = PMK_LEN;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+ sm->xxkey_len = pmk_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
- wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
+ if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
+ break;
- if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+ if (mic_len &&
+ wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len) == 0) {
ok = 1;
break;
}
- if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+#ifdef CONFIG_FILS
+ if (!mic_len &&
+ wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len, NULL) == 0) {
+ ok = 1;
+ break;
+ }
+#endif /* CONFIG_FILS */
+
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
@@ -2108,7 +2701,79 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
-#ifdef CONFIG_IEEE80211R
+ /*
+ * 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 msg 2/4 with invalid Key Data contents");
+ return;
+ }
+ if (kde.rsn_ie) {
+ eapol_key_ie = kde.rsn_ie;
+ eapol_key_ie_len = kde.rsn_ie_len;
+ } else if (kde.osen) {
+ eapol_key_ie = kde.osen;
+ eapol_key_ie_len = kde.osen_len;
+ } else {
+ eapol_key_ie = kde.wpa_ie;
+ eapol_key_ie_len = kde.wpa_ie_len;
+ }
+ ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+ if (sm->wpa_ie == NULL ||
+ wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
+ eapol_key_ie, eapol_key_ie_len)) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "WPA IE from (Re)AssocReq did not match with msg 2/4");
+ if (sm->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+ sm->wpa_ie, sm->wpa_ie_len);
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+ eapol_key_ie, eapol_key_ie_len);
+ /* MLME-DEAUTHENTICATE.request */
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+ if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+ int idx;
+ wpa_printf(MSG_DEBUG,
+ "P2P: IP address requested in EAPOL-Key exchange");
+ idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+ if (idx >= 0) {
+ u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+ bitfield_set(wpa_auth->ip_pool, idx);
+ WPA_PUT_BE32(sm->ip_addr, start + idx);
+ wpa_printf(MSG_DEBUG,
+ "P2P: Assigned IP address %u.%u.%u.%u to "
+ MACSTR, sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(sm->addr));
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
* Verify that PMKR1Name from EAPOL-Key message 2/4 matches
@@ -2127,7 +2792,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -2186,7 +2851,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
else
os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
@@ -2229,7 +2895,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
sm->TimeoutEvt = FALSE;
sm->TimeoutCtr++;
- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->TimeoutCtr > 1) {
+ /* Do not allow retransmission of EAPOL-Key msg 3/4 */
+ return;
+ }
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2259,7 +2930,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2297,12 +2969,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
kde_len += 300; /* FTIE + 2 * TIE */
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr) > 0)
kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
@@ -2314,7 +2986,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos = kde;
os_memcpy(pos, wpa_ie, wpa_ie_len);
pos += wpa_ie_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
size_t elen;
@@ -2330,7 +3002,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos -= wpa_ie_len;
pos += elen;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (gtk) {
u8 hdr[2];
hdr[0] = keyidx & 0x03;
@@ -2340,7 +3012,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
}
pos = ieee80211w_kde_add(sm, pos);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
struct wpa_auth_config *conf;
@@ -2352,7 +3024,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
- res = wpa_write_ftie(conf, conf->r0_key_holder,
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+ res = wpa_write_ftie(conf, use_sha384,
+ conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
kde + kde_len - pos,
@@ -2377,10 +3052,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
- WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
pos += 4;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr) > 0) {
u8 addr[3 * 4];
@@ -2393,7 +3068,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
#endif /* CONFIG_P2P */
wpa_send_eapol(sm->wpa_auth, sm,
- (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+ (secure ? WPA_KEY_INFO_SECURE : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
@@ -2410,7 +3087,8 @@ SM_STATE(WPA_PTK, PTKINITDONE)
int klen = wpa_cipher_key_len(sm->pairwise);
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
sm->PTK.tk, klen)) {
- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -2423,7 +3101,9 @@ SM_STATE(WPA_PTK, PTKINITDONE)
sm->wpa_auth, sm);
}
- if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 1);
}
@@ -2449,9 +3129,9 @@ SM_STATE(WPA_PTK, PTKINITDONE)
"pairwise key handshake completed (%s)",
sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
}
@@ -2495,15 +3175,22 @@ SM_STEP(WPA_PTK)
wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_keyRun) > 0)
SM_ENTER(WPA_PTK, INITPMK);
- else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
+ else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
/* FIX: && 802.1X::keyRun */)
SM_ENTER(WPA_PTK, INITPSK);
+ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
+ SM_ENTER(WPA_PTK, INITPMK);
break;
case WPA_PTK_INITPMK:
if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
- WPA_EAPOL_keyAvailable) > 0)
+ WPA_EAPOL_keyAvailable) > 0) {
SM_ENTER(WPA_PTK, PTKSTART);
- else {
+#ifdef CONFIG_DPP
+ } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_DPP */
+ } else {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"INITPMK - keyAvailable = false");
@@ -2512,9 +3199,13 @@ 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)) {
SM_ENTER(WPA_PTK, PTKSTART);
- else {
+#ifdef CONFIG_SAE
+ } else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
+ SM_ENTER(WPA_PTK, PTKSTART);
+#endif /* CONFIG_SAE */
+ } else {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"no PSK configured for the STA");
wpa_auth->dot11RSNA4WayHandshakeFailures++;
@@ -2526,11 +3217,12 @@ SM_STEP(WPA_PTK)
sm->EAPOLKeyPairwise)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
else if (sm->TimeoutCtr >
- (int) dot11RSNAConfigPairwiseUpdateCount) {
+ sm->wpa_auth->conf.wpa_pairwise_update_count) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "PTKSTART: Retry limit %d reached",
- dot11RSNAConfigPairwiseUpdateCount);
+ wpa_auth_vlogger(
+ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKSTART: Retry limit %u reached",
+ sm->wpa_auth->conf.wpa_pairwise_update_count);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
@@ -2554,12 +3246,14 @@ SM_STEP(WPA_PTK)
sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK, PTKINITDONE);
else if (sm->TimeoutCtr >
- (int) dot11RSNAConfigPairwiseUpdateCount) {
+ sm->wpa_auth->conf.wpa_pairwise_update_count ||
+ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->TimeoutCtr > 1)) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "PTKINITNEGOTIATING: Retry limit %d "
- "reached",
- dot11RSNAConfigPairwiseUpdateCount);
+ wpa_auth_vlogger(
+ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKINITNEGOTIATING: Retry limit %u reached",
+ sm->wpa_auth->conf.wpa_pairwise_update_count);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
@@ -2594,7 +3288,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
sm->GTimeoutCtr++;
- if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+ if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->GTimeoutCtr > 1) {
+ /* Do not allow retransmission of EAPOL-Key group msg 1/2 */
+ return;
+ }
+ if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2611,7 +3310,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
"sending 1/2 msg of Group Key Handshake");
gtk = gsm->GTK[gsm->GN - 1];
- if (sm->wpa_auth->conf.disable_gtk) {
+ if (sm->wpa_auth->conf.disable_gtk ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
@@ -2640,10 +3340,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
}
wpa_send_eapol(sm->wpa_auth, sm,
- WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
- rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+ rsc, NULL, kde, kde_len, gsm->GN, 1);
os_free(kde_buf);
}
@@ -2672,6 +3374,10 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
sm->Disconnect = TRUE;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "group key handshake failed (%s) after %u tries",
+ sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
+ sm->wpa_auth->conf.wpa_group_update_count);
}
@@ -2691,7 +3397,9 @@ SM_STEP(WPA_PTK_GROUP)
!sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
else if (sm->GTimeoutCtr >
- (int) dot11RSNAConfigGroupUpdateCount)
+ sm->wpa_auth->conf.wpa_group_update_count ||
+ (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
+ sm->GTimeoutCtr > 1))
SM_ENTER(WPA_PTK_GROUP, KEYERROR);
else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
@@ -2794,7 +3502,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
}
-#ifdef CONFIG_WNM
+#ifdef CONFIG_WNM_AP
/* update GTK when exiting WNM-Sleep Mode */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
{
@@ -2873,7 +3581,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
return pos - start;
}
#endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM_AP */
static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
@@ -3151,8 +3859,8 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
RSN_VERSION,
!!wpa_auth->conf.wpa_strict_rekey,
- dot11RSNAConfigGroupUpdateCount,
- dot11RSNAConfigPairwiseUpdateCount,
+ wpa_auth->conf.wpa_group_update_count,
+ wpa_auth->conf.wpa_pairwise_update_count,
wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
dot11RSNAConfigPMKLifetime,
dot11RSNAConfigPMKReauthThreshold,
@@ -3279,6 +3987,14 @@ int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm)
}
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
+{
+ if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+ return 0;
+ return sm->tk_already_set;
+}
+
+
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry)
{
@@ -3320,7 +4036,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
sm->wpa_auth->conf.disable_pmksa_caching)
return -1;
- if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
if (pmk_len > PMK_LEN_SUITE_B_192)
pmk_len = PMK_LEN_SUITE_B_192;
} else if (pmk_len > PMK_LEN) {
@@ -3372,6 +4088,29 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
}
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
+{
+ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+ sm->pmkid_set = 1;
+}
+
+
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp)
+{
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, session_timeout,
+ NULL, akmp))
+ return 0;
+
+ return -1;
+}
+
+
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
@@ -3404,12 +4143,65 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
}
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ char *buf, size_t len)
+{
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return 0;
+
+ return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
+}
+
+
struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ const u8 *pmkid, int expiration)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
+ spa, 0, NULL, WPA_KEY_MGMT_SAE);
+ if (!entry)
+ return NULL;
+
+ os_get_reltime(&now);
+ entry->expiration = now.sec + expiration;
+ return entry;
+}
+
+
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+ struct rsn_pmksa_cache_entry *entry)
+{
+ int ret;
+
+ if (!wpa_auth || !wpa_auth->pmksa)
+ return -1;
+
+ ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
+ if (ret < 0)
+ wpa_printf(MSG_DEBUG,
+ "RSN: Failed to store external PMKSA cache for "
+ MACSTR, MAC2STR(entry->spa));
+
+ return ret;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *pmkid)
{
if (!wpa_auth || !wpa_auth->pmksa)
return NULL;
- return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+ return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
}
@@ -3662,6 +4454,14 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
(timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (sm->eapol_status_cb) {
+ sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
+ sm->eapol_status_cb_ctx2);
+ sm->eapol_status_cb = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
@@ -3708,3 +4508,343 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
for (group = wpa_auth->group; group; group = group->next)
wpa_group_config_group_keys(wpa_auth, group);
}
+
+
+#ifdef CONFIG_FILS
+
+struct wpa_auth_fils_iter_data {
+ struct wpa_authenticator *auth;
+ const u8 *cache_id;
+ struct rsn_pmksa_cache_entry *pmksa;
+ const u8 *spa;
+ const u8 *pmkid;
+};
+
+
+static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
+{
+ struct wpa_auth_fils_iter_data *data = ctx;
+
+ if (a == data->auth || !a->conf.fils_cache_id_set ||
+ os_memcmp(a->conf.fils_cache_id, data->cache_id,
+ FILS_CACHE_ID_LEN) != 0)
+ return 0;
+ data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
+ return data->pmksa != NULL;
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 *pmkid)
+{
+ struct wpa_auth_fils_iter_data idata;
+
+ if (!wpa_auth->conf.fils_cache_id_set)
+ return NULL;
+ idata.auth = wpa_auth;
+ idata.cache_id = wpa_auth->conf.fils_cache_id;
+ idata.pmksa = NULL;
+ idata.spa = sta_addr;
+ idata.pmkid = pmkid;
+ wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
+ return idata.pmksa;
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
+int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
+ u8 *buf, size_t len)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+
+ return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, buf, len, NULL, 0);
+}
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+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)
+{
+ os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
+ os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
+ os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
+ *fils_kek_len = sm->PTK.kek_len;
+}
+
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ const u8 *anonce = sm->ANonce;
+ u8 anonce_buf[WPA_NONCE_LEN];
+
+ if (change_anonce) {
+ if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
+ return -1;
+ anonce = anonce_buf;
+ }
+
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/4 msg of 4-Way Handshake (TESTING)");
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
+ anonce, NULL, 0, 0, 0);
+ return 0;
+}
+
+
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+#ifdef CONFIG_IEEE80211W
+ u8 *opos;
+#endif /* CONFIG_IEEE80211W */
+ size_t gtk_len, kde_len;
+ struct wpa_group *gsm = sm->group;
+ u8 *wpa_ie;
+ int wpa_ie_len, secure, keyidx, encr = 0;
+
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+ GTK[GN], IGTK, [FTIE], [TIE * 2])
+ */
+
+ /* Use 0 RSC */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+ wpa_ie = sm->wpa_auth->wpa_ie;
+ wpa_ie_len = sm->wpa_auth->wpa_ie_len;
+ if (sm->wpa == WPA_VERSION_WPA &&
+ (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
+ /* WPA-only STA, remove RSN IE and possible MDIE */
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ wpa_ie_len = wpa_ie[1] + 2;
+ }
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 3/4 msg of 4-Way Handshake (TESTING)");
+ if (sm->wpa == WPA_VERSION_WPA2) {
+ /* WPA2 send GTK in the 4-way handshake */
+ secure = 1;
+ gtk = gsm->GTK[gsm->GN - 1];
+ gtk_len = gsm->GTK_len;
+ keyidx = gsm->GN;
+ _rsc = rsc;
+ encr = 1;
+ } else {
+ /* WPA does not include GTK in msg 3/4 */
+ secure = 0;
+ gtk = NULL;
+ gtk_len = 0;
+ keyidx = 0;
+ _rsc = NULL;
+ if (sm->rx_eapol_key_secure) {
+ /*
+ * It looks like Windows 7 supplicant tries to use
+ * Secure bit in msg 2/4 after having reported Michael
+ * MIC failure and it then rejects the 4-way handshake
+ * if msg 3/4 does not set Secure bit. Work around this
+ * by setting the Secure bit here even in the case of
+ * WPA if the supplicant used it first.
+ */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "STA used Secure bit in WPA msg 2/4 - "
+ "set Secure for 3/4 as workaround");
+ secure = 1;
+ }
+ }
+
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ if (gtk)
+ kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+ kde_len += 300; /* FTIE + 2 * TIE */
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ kde = os_malloc(kde_len);
+ if (kde == NULL)
+ return -1;
+
+ pos = kde;
+ os_memcpy(pos, wpa_ie, wpa_ie_len);
+ pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ size_t elen;
+
+ elen = pos - kde;
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert "
+ "PMKR1Name into RSN IE in EAPOL-Key data");
+ os_free(kde);
+ return -1;
+ }
+ pos -= wpa_ie_len;
+ pos += elen;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+ if (gtk) {
+ u8 hdr[2];
+ hdr[0] = keyidx & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gtk_len);
+ }
+#ifdef CONFIG_IEEE80211W
+ opos = pos;
+ pos = ieee80211w_kde_add(sm, pos);
+ if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
+ os_memset(opos, 0, 6); /* clear PN */
+ }
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ struct wpa_auth_config *conf;
+
+ conf = &sm->wpa_auth->conf;
+ if (sm->assoc_resp_ftie &&
+ kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+ os_memcpy(pos, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]);
+ res = 2 + sm->assoc_resp_ftie[1];
+ } else {
+ int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
+ res = wpa_write_ftie(conf, use_sha384,
+ conf->r0_key_holder,
+ conf->r0_key_holder_len,
+ NULL, NULL, pos,
+ kde + kde_len - pos,
+ NULL, 0);
+ }
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+ "into EAPOL-Key Key Data");
+ os_free(kde);
+ return -1;
+ }
+ pos += res;
+
+ /* TIE[ReassociationDeadline] (TU) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+ WPA_PUT_LE32(pos, conf->reassociation_deadline);
+ pos += 4;
+
+ /* TIE[KeyLifetime] (seconds) */
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(pos, conf->r0_key_lifetime);
+ pos += 4;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ (secure ? WPA_KEY_INFO_SECURE : 0) |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
+ WPA_KEY_INFO_KEY_TYPE,
+ _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+ os_free(kde);
+ return 0;
+}
+
+
+int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2)
+{
+ u8 rsc[WPA_KEY_RSC_LEN];
+ struct wpa_group *gsm = sm->group;
+ const u8 *kde;
+ u8 *kde_buf = NULL, *pos, hdr[2];
+#ifdef CONFIG_IEEE80211W
+ u8 *opos;
+#endif /* CONFIG_IEEE80211W */
+ size_t kde_len;
+ u8 *gtk;
+
+ /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
+ os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+ /* Use 0 RSC */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "sending 1/2 msg of Group Key Handshake (TESTING)");
+
+ 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);
+ kde_buf = os_malloc(kde_len);
+ if (kde_buf == NULL)
+ return -1;
+
+ kde = pos = kde_buf;
+ hdr[0] = gsm->GN & 0x03;
+ hdr[1] = 0;
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+ gtk, gsm->GTK_len);
+#ifdef CONFIG_IEEE80211W
+ opos = pos;
+ pos = ieee80211w_kde_add(sm, pos);
+ if (pos - opos >=
+ 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
+ /* skip KDE header and keyid */
+ opos += 2 + RSN_SELECTOR_LEN + 2;
+ os_memset(opos, 0, 6); /* clear PN */
+ }
+#endif /* CONFIG_IEEE80211W */
+ kde_len = pos - kde;
+ } else {
+ kde = gtk;
+ kde_len = gsm->GTK_len;
+ }
+
+ sm->eapol_status_cb = cb;
+ sm->eapol_status_cb_ctx1 = ctx1;
+ sm->eapol_status_cb_ctx2 = ctx2;
+
+ wpa_send_eapol(sm->wpa_auth, sm,
+ WPA_KEY_INFO_SECURE |
+ (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
+ WPA_KEY_INFO_MIC : 0) |
+ WPA_KEY_INFO_ACK |
+ (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
+ rsc, NULL, kde, kde_len, gsm->GN, 1);
+
+ os_free(kde_buf);
+ return 0;
+}
+
+
+int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
+{
+ if (!wpa_auth)
+ return -1;
+ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 97461b029d24..fad5536f740e 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen
+ * Copyright (c) 2004-2017, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,6 +14,8 @@
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
+struct vlan_description;
+
#define MAX_OWN_IE_OVERRIDE 256
#ifdef _MSC_VER
@@ -37,73 +39,99 @@ struct ft_rrb_frame {
#define FT_PACKET_REQUEST 0
#define FT_PACKET_RESPONSE 1
-/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
-#define FT_PACKET_R0KH_R1KH_PULL 200
-#define FT_PACKET_R0KH_R1KH_RESP 201
-#define FT_PACKET_R0KH_R1KH_PUSH 202
-#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
-#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
- WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \
- ETH_ALEN)
-#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8)
+/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These
+ * use OUI Extended EtherType as the encapsulating format. */
+#define FT_PACKET_R0KH_R1KH_PULL 0x01
+#define FT_PACKET_R0KH_R1KH_RESP 0x02
+#define FT_PACKET_R0KH_R1KH_PUSH 0x03
+#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04
+#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05
-struct ft_r0kh_r1kh_pull_frame {
- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
- u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
- le16 data_length; /* little endian length of data (44) */
- u8 ap_address[ETH_ALEN];
+/* packet layout
+ * IEEE 802 extended OUI ethertype frame header
+ * u16 authlen (little endian)
+ * multiple of struct ft_rrb_tlv (authenticated only, length = authlen)
+ * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra
+ * blocksize length)
+ *
+ * AES-SIV AAD;
+ * source MAC address (6)
+ * authenticated-only TLVs (authlen)
+ * subtype (1; FT_PACKET_*)
+ */
- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
- u8 pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 r1kh_id[FT_R1KH_ID_LEN];
- u8 s1kh_id[ETH_ALEN];
- u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */
- u8 key_wrap_extra[8];
+#define FT_RRB_NONCE_LEN 16
+
+#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */
+
+#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */
+#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */
+#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */
+
+#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */
+#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */
+#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */
+
+#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R0 8 /* PMK_LEN */
+#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */
+#define FT_RRB_PMK_R1 10 /* PMK_LEN */
+
+#define FT_RRB_PAIRWISE 11 /* le16 */
+#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */
+
+#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */
+#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */
+
+#define FT_RRB_IDENTITY 15
+#define FT_RRB_RADIUS_CUI 16
+#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */
+
+struct ft_rrb_tlv {
+ le16 type;
+ le16 len;
+ /* followed by data of length len */
} STRUCT_PACKED;
-#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
- FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
- WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_resp_frame {
- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
- u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
- le16 data_length; /* little endian length of data (78) */
- u8 ap_address[ETH_ALEN];
-
- u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
- u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
- u8 s1kh_id[ETH_ALEN]; /* copied from pull */
- u8 pmk_r1[PMK_LEN];
- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
- le16 pairwise;
- u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
- u8 key_wrap_extra[8];
+struct ft_rrb_seq {
+ le32 dom;
+ le32 seq;
+ le32 ts;
} STRUCT_PACKED;
-#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
- WPA_PMK_NAME_LEN + PMK_LEN + \
- WPA_PMK_NAME_LEN + 2)
-#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8)
-struct ft_r0kh_r1kh_push_frame {
- u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
- u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
- le16 data_length; /* little endian length of data (82) */
- u8 ap_address[ETH_ALEN];
-
- /* Encrypted with AES key-wrap */
- u8 timestamp[4]; /* current time in seconds since unix epoch, little
- * endian */
- u8 r1kh_id[FT_R1KH_ID_LEN];
- u8 s1kh_id[ETH_ALEN];
- u8 pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
- u8 pmk_r1_name[WPA_PMK_NAME_LEN];
- le16 pairwise;
- u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
- u8 key_wrap_extra[8];
-} STRUCT_PACKED;
+/* session TLVs:
+ * required: PMK_R1, PMK_R1_NAME, PAIRWISE
+ * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI,
+ * SESSION_TIMEOUT
+ *
+ * pull frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: PMK_R0_NAME, S1KH_ID
+ *
+ * response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: S1KH_ID
+ * optional: session TLVs
+ *
+ * push frame TLVs:
+ * auth:
+ * required: SEQ, R0KH_ID, R1KH_ID
+ * encrypted:
+ * required: S1KH_ID, PMK_R0_NAME, session TLVs
+ *
+ * sequence number request frame TLVs:
+ * auth:
+ * required: R0KH_ID, R1KH_ID, NONCE
+ *
+ * sequence number response frame TLVs:
+ * auth:
+ * required: SEQ, NONCE, R0KH_ID, R1KH_ID
+ */
#ifdef _MSC_VER
#pragma pack(pop)
@@ -116,6 +144,7 @@ struct wpa_authenticator;
struct wpa_state_machine;
struct rsn_pmksa_cache_entry;
struct eapol_state_machine;
+struct ft_remote_seq;
struct ft_remote_r0kh {
@@ -123,7 +152,8 @@ struct ft_remote_r0kh {
u8 addr[ETH_ALEN];
u8 id[FT_R0KH_ID_MAX_LEN];
size_t id_len;
- u8 key[16];
+ u8 key[32];
+ struct ft_remote_seq *seq;
};
@@ -131,7 +161,8 @@ struct ft_remote_r1kh {
struct ft_remote_r1kh *next;
u8 addr[ETH_ALEN];
u8 id[FT_R1KH_ID_LEN];
- u8 key[16];
+ u8 key[32];
+ struct ft_remote_seq *seq;
};
@@ -144,10 +175,12 @@ struct wpa_auth_config {
int wpa_strict_rekey;
int wpa_gmk_rekey;
int wpa_ptk_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
+ int wpa_disable_eapol_key_retries;
int rsn_pairwise;
int rsn_preauth;
int eapol_version;
- int peerkey;
int wmm_enabled;
int wmm_uapsd;
int disable_pmksa_caching;
@@ -156,21 +189,28 @@ struct wpa_auth_config {
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
int group_mgmt_cipher;
+ int sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
size_t r0_key_holder_len;
u8 r1_key_holder[FT_R1KH_ID_LEN];
- u32 r0_key_lifetime;
+ u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
+ int rkh_pos_timeout;
+ int rkh_neg_timeout;
+ int rkh_pull_timeout; /* ms */
+ int rkh_pull_retries;
+ int r1_max_key_lifetime;
u32 reassociation_deadline;
- struct ft_remote_r0kh *r0kh_list;
- struct ft_remote_r1kh *r1kh_list;
+ struct ft_remote_r0kh **r0kh_list;
+ struct ft_remote_r1kh **r1kh_list;
int pmk_r1_push;
int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+ int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
int disable_gtk;
int ap_mlme;
#ifdef CONFIG_TESTING_OPTIONS
@@ -184,6 +224,10 @@ struct wpa_auth_config {
u8 ip_addr_start[4];
u8 ip_addr_end[4];
#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+ unsigned int fils_cache_id_set:1;
+ u8 fils_cache_id[FILS_CACHE_ID_LEN];
+#endif /* CONFIG_FILS */
};
typedef enum {
@@ -197,7 +241,6 @@ typedef enum {
} wpa_eapol_variable;
struct wpa_auth_callbacks {
- void *ctx;
void (*logger)(void *ctx, const u8 *addr, logger_level level,
const char *txt);
void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
@@ -207,7 +250,7 @@ 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);
+ const u8 *prev_psk, size_t *psk_len);
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);
@@ -220,13 +263,29 @@ struct wpa_auth_callbacks {
void *ctx), void *cb_ctx);
int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
size_t data_len);
-#ifdef CONFIG_IEEE80211R
+ int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
+ size_t data_len);
+#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,
+ struct vlan_description *vlan);
+ int (*get_vlan)(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan);
+ int (*set_identity)(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len);
+ size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ int (*set_radius_cui)(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len);
+ size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf);
+ void (*set_session_timeout)(void *ctx, const u8 *sta_addr,
+ int session_timeout);
+ int (*get_session_timeout)(void *ctx, const u8 *sta_addr);
+
int (*send_ft_action)(void *ctx, const u8 *dst,
const u8 *data, size_t data_len);
int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
size_t tspec_ielen);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_MESH
int (*start_ampe)(void *ctx, const u8 *sta_addr);
#endif /* CONFIG_MESH */
@@ -234,7 +293,8 @@ struct wpa_auth_callbacks {
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
- struct wpa_auth_callbacks *cb);
+ const struct wpa_auth_callbacks *cb,
+ void *cb_ctx);
int wpa_init_keys(struct wpa_authenticator *wpa_auth);
void wpa_deinit(struct wpa_authenticator *wpa_auth);
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
@@ -244,13 +304,14 @@ enum {
WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
- WPA_INVALID_MDIE, WPA_INVALID_PROTO
+ WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID
};
-
+
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
- const u8 *mdie, size_t mdie_len);
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
@@ -267,7 +328,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
u8 *data, size_t data_len);
enum wpa_event {
WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
- WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED
+ WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED
};
void wpa_remove_ptk(struct wpa_state_machine *sm);
int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
@@ -281,6 +342,7 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
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);
+int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm);
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry *
@@ -297,13 +359,28 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid);
+void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
+int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int session_timeout, int akmp);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
size_t len);
void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ char *buf, size_t len);
struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ const u8 *pmkid, int expiration);
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+ struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *pmkid);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, const u8 *pmkid);
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
@@ -312,7 +389,7 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len);
@@ -327,8 +404,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
const u8 *data, size_t data_len);
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+ size_t data_len);
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
-#endif /* CONFIG_IEEE80211R */
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth);
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm);
+#endif /* CONFIG_IEEE80211R_AP */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
@@ -347,5 +429,44 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *snonce, const u8 *anonce,
+ const u8 *dhss, size_t dhss_len,
+ struct wpabuf *g_sta, struct wpabuf *g_ap);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+ const struct ieee80211_mgmt *mgmt, size_t frame_len,
+ u8 *pos, size_t left);
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+ size_t current_len, size_t max_len,
+ const struct wpabuf *hlp);
+int fils_set_tk(struct wpa_state_machine *sm);
+u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid,
+ const u8 *fils_session,
+ struct wpabuf *fils_hlp_resp);
+const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *fils_session);
+int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
+ size_t ies_len);
+
+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);
+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);
+
+int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+int wpa_auth_resend_m3(struct wpa_state_machine *sm,
+ void (*cb)(void *ctx1, void *ctx2),
+ void *ctx1, void *ctx2);
+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);
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e63b99ad2034..f6792e00f32d 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2015, Jouni Malinen
+ * Copyright (c) 2004-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,7 +13,10 @@
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
+#include "crypto/sha384.h"
#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -22,41 +25,692 @@
#include "wpa_auth_i.h"
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+
+const unsigned int ftRRBseqTimeout = 10;
+const unsigned int ftRRBmaxQueueLen = 100;
+
static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
const u8 *current_ap, const u8 *sta_addr,
u16 status, const u8 *resp_ies,
size_t resp_ies_len);
+static void ft_finish_pull(struct wpa_state_machine *sm);
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
+struct tlv_list {
+ u16 type;
+ size_t len;
+ const u8 *data;
+};
+
+
+/**
+ * wpa_ft_rrb_decrypt - Decrypt FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @enc: Pointer to encrypted TLVs
+ * @enc_len: Length of encrypted TLVs in octets
+ * @auth: Pointer to authenticated TLVs
+ * @auth_len: Length of authenticated TLVs in octets
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @plain: Pointer to return the pointer to the allocated plaintext buffer;
+ * needs to be freed by the caller if not NULL;
+ * will only be returned on success
+ * @plain_len: Pointer to return the length of the allocated plaintext buffer
+ * in octets
+ * 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 *auth, const size_t auth_len,
+ const u8 *src_addr, u8 type,
+ u8 **plain, size_t *plain_size)
+{
+ const u8 *ad[3] = { src_addr, auth, &type };
+ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+
+ wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+
+ if (!key) { /* skip decryption */
+ *plain = os_memdup(enc, enc_len);
+ if (enc_len > 0 && !*plain)
+ goto err;
+
+ *plain_size = enc_len;
+
+ return 0;
+ }
+
+ *plain = NULL;
+
+ /* SIV overhead */
+ if (enc_len < AES_BLOCK_SIZE)
+ goto err;
+
+ *plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
+ if (!*plain)
+ goto err;
+
+ 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",
+ *plain, *plain_size);
+ return 0;
+err:
+ os_free(*plain);
+ *plain = NULL;
+ *plain_size = 0;
+
+ wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
+
+ return -1;
+}
+
+
+/* get first tlv record in packet matching type
+ * @data (decrypted) packet
+ * @return 0 on success else -1
+ */
+static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
+ u16 type, size_t *tlv_len, const u8 **tlv_data)
+{
+ const struct ft_rrb_tlv *f;
+ size_t left;
+ le16 type16;
+ size_t len;
+
+ left = plain_len;
+ type16 = host_to_le16(type);
+
+ while (left >= sizeof(*f)) {
+ f = (const struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+ len = le_to_host16(f->len);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ break;
+ }
+
+ if (f->type == type16) {
+ *tlv_len = len;
+ *tlv_data = plain;
+ return 0;
+ }
+
+ left -= len;
+ plain += len;
+ }
+
+ return -1;
+}
+
+
+static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
+{
+ const struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+
+ left = plain_len;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB dump message");
+ while (left >= sizeof(*f)) {
+ f = (const struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+ len = le_to_host16(f->len);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
+ le_to_host16(f->type), len);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB message truncated: left %zu bytes, need %zu",
+ left, len);
+ break;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
+
+ left -= len;
+ plain += len;
+ }
+
+ if (left > 0)
+ wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
+
+ wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
+}
+
+
+static int cmp_int(const void *a, const void *b)
+{
+ int x, y;
+
+ x = *((int *) a);
+ y = *((int *) b);
+ return x - y;
+}
+
+
+static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
+ struct vlan_description *vlan)
+{
+ struct ft_rrb_tlv *f;
+ size_t left;
+ size_t len;
+ int taggedidx;
+ int vlan_id;
+ int type;
+
+ left = plain_len;
+ taggedidx = 0;
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ while (left >= sizeof(*f)) {
+ f = (struct ft_rrb_tlv *) plain;
+
+ left -= sizeof(*f);
+ plain += sizeof(*f);
+
+ len = le_to_host16(f->len);
+ type = le_to_host16(f->type);
+
+ if (left < len) {
+ wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
+ return -1;
+ }
+
+ if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
+ goto skip;
+
+ if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_UNTAGGED invalid length");
+ return -1;
+ }
+
+ if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN_TAGGED invalid length");
+ return -1;
+ }
+
+ while (len >= sizeof(le16)) {
+ vlan_id = WPA_GET_LE16(plain);
+ plain += sizeof(le16);
+ left -= sizeof(le16);
+ len -= sizeof(le16);
+
+ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB VLAN ID invalid %d",
+ vlan_id);
+ continue;
+ }
+
+ if (type == FT_RRB_VLAN_UNTAGGED)
+ vlan->untagged = vlan_id;
+
+ if (type == FT_RRB_VLAN_TAGGED &&
+ taggedidx < MAX_NUM_TAGGED_VLAN) {
+ vlan->tagged[taggedidx] = vlan_id;
+ taggedidx++;
+ } else if (type == FT_RRB_VLAN_TAGGED) {
+ wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
+ }
+ }
+
+ skip:
+ left -= len;
+ plain += len;
+ }
+
+ if (taggedidx)
+ qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
+
+ vlan->notempty = vlan->untagged || vlan->tagged[0];
+
+ return 0;
+}
+
+
+static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!tlvs)
+ return 0;
+
+ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += tlvs[i].len;
+ }
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
+ u8 *endpos)
+{
+ int i;
+ size_t tlv_len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos;
+
+ if (!tlvs)
+ return 0;
+
+ tlv_len = 0;
+ pos = start;
+ for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
+ if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
+ return tlv_len;
+ tlv_len += sizeof(*hdr);
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(tlvs[i].type);
+ hdr->len = host_to_le16(tlvs[i].len);
+ pos = start + tlv_len;
+
+ if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
+ return tlv_len;
+ if (tlvs[i].len == 0)
+ continue;
+ tlv_len += tlvs[i].len;
+ os_memcpy(pos, tlvs[i].data, tlvs[i].len);
+ pos = start + tlv_len;
+ }
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
+{
+ size_t tlv_len = 0;
+ int i;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ if (vlan->untagged) {
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ tlv_len += sizeof(le16);
+ }
+ if (vlan->tagged[0])
+ tlv_len += sizeof(struct ft_rrb_tlv);
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
+ tlv_len += sizeof(le16);
+
+ return tlv_len;
+}
+
+
+static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
+ u8 *start, u8 *endpos)
+{
+ size_t tlv_len;
+ int i, len;
+ struct ft_rrb_tlv *hdr;
+ u8 *pos = start;
+
+ if (!vlan || !vlan->notempty)
+ return 0;
+
+ tlv_len = 0;
+ if (vlan->untagged) {
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
+ hdr->len = host_to_le16(sizeof(le16));
+ pos = start + tlv_len;
+
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ WPA_PUT_LE16(pos, vlan->untagged);
+ pos = start + tlv_len;
+ }
+
+ if (!vlan->tagged[0])
+ return tlv_len;
+
+ tlv_len += sizeof(*hdr);
+ if (start + tlv_len > endpos)
+ return tlv_len;
+ hdr = (struct ft_rrb_tlv *) pos;
+ hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
+ len = 0; /* len is computed below */
+ pos = start + tlv_len;
+
+ for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
+ tlv_len += sizeof(le16);
+ if (start + tlv_len > endpos)
+ break;
+ len += sizeof(le16);
+ WPA_PUT_LE16(pos, vlan->tagged[i]);
+ pos = start + tlv_len;
+ }
+
+ hdr->len = host_to_le16(len);
+
+ return tlv_len;
+}
+
+
+static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
+ const struct tlv_list *tlvs2,
+ const struct vlan_description *vlan,
+ u8 **plain, size_t *plain_len)
+{
+ u8 *pos, *endpos;
+ size_t tlv_len;
+
+ tlv_len = wpa_ft_tlv_len(tlvs1);
+ tlv_len += wpa_ft_tlv_len(tlvs2);
+ tlv_len += wpa_ft_vlan_len(vlan);
+
+ *plain_len = tlv_len;
+ *plain = os_zalloc(tlv_len);
+ if (!*plain) {
+ wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
+ goto err;
+ }
+
+ pos = *plain;
+ endpos = *plain + tlv_len;
+ pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
+ pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
+ pos += wpa_ft_vlan_lin(vlan, pos, endpos);
+
+ /* sanity check */
+ if (pos != endpos) {
+ wpa_printf(MSG_ERROR, "FT: Length error building RRB");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ os_free(*plain);
+ *plain = NULL;
+ *plain_len = 0;
+ return -1;
+}
+
+
+static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
+ const u8 *plain, const size_t plain_len,
+ const u8 *auth, const size_t auth_len,
+ const u8 *src_addr, u8 type, u8 *enc)
+{
+ const u8 *ad[3] = { src_addr, auth, &type };
+ size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(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);
+
+ if (!key) {
+ /* encryption not needed, return plaintext as packet */
+ os_memcpy(enc, plain, plain_len);
+ } else if (aes_siv_encrypt(key, key_len, plain, plain_len,
+ 3, ad, ad_len, enc) < 0) {
+ wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_rrb_build - Build and encrypt an FT RRB message
+ * @key: AES-SIV key for AEAD
+ * @key_len: Length of key in octets
+ * @tlvs_enc0: First set of to-be-encrypted TLVs
+ * @tlvs_enc1: Second set of to-be-encrypted TLVs
+ * @tlvs_auth: Set of to-be-authenticated TLVs
+ * @src_addr: MAC address of the frame sender
+ * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
+ * @packet Pointer to return the pointer to the allocated packet buffer;
+ * needs to be freed by the caller if not null;
+ * will only be returned on success
+ * @packet_len: Pointer to return the length of the allocated buffer in octets
+ * Returns: 0 on success, -1 on error
+ */
+static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
+ const struct tlv_list *tlvs_enc0,
+ const struct tlv_list *tlvs_enc1,
+ const struct tlv_list *tlvs_auth,
+ const struct vlan_description *vlan,
+ const u8 *src_addr, u8 type,
+ u8 **packet, size_t *packet_len)
+{
+ u8 *plain = NULL, *auth = NULL, *pos;
+ size_t plain_len = 0, auth_len = 0;
+ int ret = -1;
+
+ *packet = NULL;
+ if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
+ goto out;
+
+ if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
+ goto out;
+
+ *packet_len = sizeof(u16) + auth_len + plain_len;
+ if (key)
+ *packet_len += AES_BLOCK_SIZE;
+ *packet = os_zalloc(*packet_len);
+ if (!*packet)
+ goto out;
+
+ pos = *packet;
+ WPA_PUT_LE16(pos, auth_len);
+ pos += 2;
+ os_memcpy(pos, auth, auth_len);
+ pos += auth_len;
+ if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
+ auth_len, src_addr, type, pos) < 0)
+ goto out;
+
+ ret = 0;
+
+out:
+ bin_clear_free(plain, plain_len);
+ os_free(auth);
+
+ if (ret) {
+ wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
+ os_free(*packet);
+ *packet = NULL;
+ *packet_len = 0;
+ }
+
+ return ret;
+}
+
+
+#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
+ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+ &f_##field##_len, &f_##field) < 0 || \
+ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+ wpa_printf(MSG_INFO, "FT: Missing required " #field \
+ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+ wpa_ft_rrb_dump(srcfield, srcfield##_len); \
+ goto out; \
+ } \
+} while (0)
+
+#define RRB_GET(type, field, txt, checklength) \
+ RRB_GET_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_AUTH(type, field, txt, checklength) \
+ RRB_GET_SRC(auth, type, field, txt, checklength)
+
+#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
+ if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
+ &f_##field##_len, &f_##field) < 0 || \
+ (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
+ wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
+ " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
+ f_##field##_len = 0; \
+ f_##field = NULL; \
+ } \
+} while (0)
+
+#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
+ RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
+#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
+ RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
- if (wpa_auth->cb.send_ether == NULL)
+ if (wpa_auth->cb->send_ether == NULL)
return -1;
wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
- return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
- data, data_len);
+ return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
+ data, data_len);
+}
+
+
+static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
+ const u8 *dst, u8 oui_suffix,
+ const u8 *data, size_t data_len)
+{
+ if (!wpa_auth->cb->send_oui)
+ return -1;
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR,
+ oui_suffix, MAC2STR(dst));
+ return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
+ data_len);
}
static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
const u8 *dst, const u8 *data, size_t data_len)
{
- if (wpa_auth->cb.send_ft_action == NULL)
+ if (wpa_auth->cb->send_ft_action == NULL)
return -1;
- return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
- data, data_len);
+ return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
+ data, data_len);
+}
+
+
+static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
+{
+ if (wpa_auth->cb->get_psk == NULL)
+ return NULL;
+ return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+ prev_psk, NULL);
}
static struct wpa_state_machine *
wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
{
- if (wpa_auth->cb.add_sta == NULL)
+ if (wpa_auth->cb->add_sta == NULL)
return NULL;
- return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
+ return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
+}
+
+
+static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->set_vlan)
+ return -1;
+ return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, struct vlan_description *vlan)
+{
+ if (!wpa_auth->cb->get_vlan)
+ return -1;
+ return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
+}
+
+
+static int
+wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ if (!wpa_auth->cb->set_identity)
+ return -1;
+ return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
+ identity_len);
+}
+
+
+static size_t
+wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_identity)
+ return 0;
+ return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static int
+wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ if (!wpa_auth->cb->set_radius_cui)
+ return -1;
+ return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
+ radius_cui, radius_cui_len);
+}
+
+
+static size_t
+wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+ const u8 **buf)
+{
+ *buf = NULL;
+ if (!wpa_auth->cb->get_radius_cui)
+ return 0;
+ return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
+}
+
+
+static void
+wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr, int session_timeout)
+{
+ if (!wpa_auth->cb->set_session_timeout)
+ return;
+ wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
+ session_timeout);
+}
+
+
+static int
+wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+{
+ if (!wpa_auth->cb->get_session_timeout)
+ return 0;
+ return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
}
@@ -64,12 +718,12 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr,
u8 *tspec_ie, size_t tspec_ielen)
{
- if (wpa_auth->cb.add_tspec == NULL) {
+ if (wpa_auth->cb->add_tspec == NULL) {
wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
return -1;
}
- return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
- tspec_ielen);
+ return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
+ tspec_ielen);
}
@@ -93,30 +747,44 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
}
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
- size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+ const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len)
{
u8 *pos = buf, *ielen;
- struct rsn_ftie *hdr;
+ size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
+ sizeof(struct rsn_ftie);
- if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
+ if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
subelem_len)
return -1;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ielen = pos++;
- hdr = (struct rsn_ftie *) pos;
- os_memset(hdr, 0, sizeof(*hdr));
- pos += sizeof(*hdr);
- WPA_PUT_LE16(hdr->mic_control, 0);
- if (anonce)
- os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
- if (snonce)
- os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ WPA_PUT_LE16(hdr->mic_control, 0);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
+
+ os_memset(hdr, 0, sizeof(*hdr));
+ pos += sizeof(*hdr);
+ WPA_PUT_LE16(hdr->mic_control, 0);
+ if (anonce)
+ os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
+ if (snonce)
+ os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
+ }
/* Optional Parameters */
*pos++ = FTIE_SUBELEM_R1KH_ID;
@@ -142,35 +810,432 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
}
+/* A packet to be handled after seq response */
+struct ft_remote_item {
+ struct dl_list list;
+
+ u8 nonce[FT_RRB_NONCE_LEN];
+ struct os_reltime nonce_ts;
+
+ u8 src_addr[ETH_ALEN];
+ u8 *enc;
+ size_t enc_len;
+ u8 *auth;
+ size_t auth_len;
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer);
+};
+
+
+static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
+ dl_list_del(&item->list);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item->auth);
+ os_free(item);
+}
+
+
+static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, int cb)
+{
+ struct ft_remote_item *item, *n;
+
+ dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
+ struct ft_remote_item, list) {
+ if (cb && item->cb)
+ item->cb(wpa_auth, item->src_addr, item->enc,
+ item->enc_len, item->auth, item->auth_len, 1);
+ wpa_ft_rrb_seq_free(item);
+ }
+}
+
+
+static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct ft_remote_item *item = timeout_ctx;
+
+ wpa_ft_rrb_seq_free(item);
+}
+
+
+static int
+wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ const u8 *f_r1kh_id, const u8 *key, size_t key_len,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ struct ft_remote_item *item = NULL;
+ u8 *packet = NULL;
+ size_t packet_len;
+ struct tlv_list seq_req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = NULL /* to be filled: item->nonce */ },
+ { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
+ .data = f_r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
+ wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
+ goto err;
+ }
+
+ item = os_zalloc(sizeof(*item));
+ if (!item)
+ goto err;
+
+ os_memcpy(item->src_addr, src_addr, ETH_ALEN);
+ item->cb = cb;
+
+ if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
+ goto err;
+ }
+
+ if (os_get_reltime(&item->nonce_ts) < 0)
+ goto err;
+
+ if (enc && enc_len > 0) {
+ item->enc = os_memdup(enc, enc_len);
+ item->enc_len = enc_len;
+ if (!item->enc)
+ goto err;
+ }
+
+ if (auth && auth_len > 0) {
+ item->auth = os_memdup(auth, auth_len);
+ item->auth_len = auth_len;
+ if (!item->auth)
+ goto err;
+ }
+
+ eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
+ wpa_auth, item);
+
+ seq_req_auth[0].data = item->nonce;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ &packet, &packet_len) < 0) {
+ item = NULL; /* some other seq resp might still accept this */
+ goto err;
+ }
+
+ dl_list_add(&rkh_seq->rx.queue, &item->list);
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ packet, packet_len);
+
+ os_free(packet);
+
+ return 0;
+err:
+ wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
+ if (item) {
+ os_free(item->auth);
+ bin_clear_free(item->enc, item->enc_len);
+ os_free(item);
+ }
+
+ return -1;
+}
+
+
+#define FT_RRB_SEQ_OK 0
+#define FT_RRB_SEQ_DROP 1
+#define FT_RRB_SEQ_DEFER 2
+
+static int
+wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, int no_defer)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, rkh_off;
+ struct os_reltime now;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ if (rkh_seq->rx.num_last == 0) {
+ /* first packet from remote */
+ goto defer;
+ }
+
+ if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
+ /* remote might have rebooted */
+ goto defer;
+ }
+
+ if (os_get_reltime(&now) == 0) {
+ u32 msg_ts_now_remote, msg_ts_off;
+ struct os_reltime now_remote;
+
+ os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
+ msg_ts_now_remote = now_remote.sec;
+ msg_ts_off = le_to_host32(msg_both->ts) -
+ (msg_ts_now_remote - ftRRBseqTimeout);
+ if (msg_ts_off > 2 * ftRRBseqTimeout)
+ goto defer;
+ }
+
+ msg_seq = le_to_host32(msg_both->seq);
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ msg_off = msg_seq - rkh_off;
+ if (msg_off > 0xC0000000)
+ goto out; /* too old message, drop it */
+
+ if (msg_off <= 0x40000000) {
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ if (rkh_seq->rx.last[i] == msg_seq)
+ goto out; /* duplicate message, drop it */
+ }
+
+ return FT_RRB_SEQ_OK;
+ }
+
+defer:
+ if (no_defer)
+ goto out;
+
+ wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
+ MACSTR, msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DEFER;
+out:
+ wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
+ msgtype, MAC2STR(src_addr));
+
+ return FT_RRB_SEQ_DROP;
+}
+
+
+static void
+wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_seq *rkh_seq, const u8 *src_addr,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype)
+{
+ const u8 *f_seq;
+ size_t f_seq_len;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_seq, msg_off, min_off, rkh_off;
+ int minidx = 0;
+ unsigned int i;
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_seq = le_to_host32(msg_both->seq);
+
+ if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
+ rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
+ rkh_seq->rx.num_last++;
+ return;
+ }
+
+ rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
+ for (i = 0; i < rkh_seq->rx.num_last; i++) {
+ msg_off = rkh_seq->rx.last[i] - rkh_off;
+ min_off = rkh_seq->rx.last[minidx] - rkh_off;
+ if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
+ minidx = i;
+ }
+ rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
+ rkh_seq->rx.offsetidx = minidx;
+
+ return;
+out:
+ /* RRB_GET_AUTH should never fail here as
+ * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
+ wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
+}
+
+
+static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
+ struct ft_rrb_seq *f_seq)
+{
+ struct os_reltime now;
+
+ if (os_get_reltime(&now) < 0)
+ return -1;
+
+ if (!rkh_seq->tx.dom) {
+ if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
+ sizeof(rkh_seq->tx.seq))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.seq = now.usec;
+ }
+ if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
+ sizeof(rkh_seq->tx.dom))) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to get random data for sequence number initialization");
+ rkh_seq->tx.dom = now.usec;
+ }
+ rkh_seq->tx.dom |= 1;
+ }
+
+ f_seq->dom = host_to_le32(rkh_seq->tx.dom);
+ f_seq->seq = host_to_le32(rkh_seq->tx.seq);
+ f_seq->ts = host_to_le32(now.sec);
+
+ rkh_seq->tx.seq++;
+
+ return 0;
+}
+
+
struct wpa_ft_pmk_r0_sa {
- struct wpa_ft_pmk_r0_sa *next;
- u8 pmk_r0[PMK_LEN];
+ struct dl_list list;
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+ struct vlan_description *vlan;
+ os_time_t expiration; /* 0 for no expiration */
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
int pmk_r1_pushed;
};
struct wpa_ft_pmk_r1_sa {
- struct wpa_ft_pmk_r1_sa *next;
- u8 pmk_r1[PMK_LEN];
+ struct dl_list list;
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
- /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
+ struct vlan_description *vlan;
+ u8 *identity;
+ size_t identity_len;
+ u8 *radius_cui;
+ size_t radius_cui_len;
+ os_time_t session_timeout; /* 0 for no expiration */
+ /* TODO: radius_class, EAP type */
};
struct wpa_ft_pmk_cache {
- struct wpa_ft_pmk_r0_sa *pmk_r0;
- struct wpa_ft_pmk_r1_sa *pmk_r1;
+ struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
+ struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
};
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
+{
+ if (!r0)
+ return;
+
+ dl_list_del(&r0->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+
+ os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
+ os_free(r0->vlan);
+ os_free(r0->identity);
+ os_free(r0->radius_cui);
+ os_free(r0);
+}
+
+
+static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
+ struct os_reltime now;
+ int expires_in;
+ int session_timeout;
+
+ os_get_reltime(&now);
+
+ if (!r0)
+ return;
+
+ expires_in = r0->expiration - now.sec;
+ session_timeout = r0->session_timeout - now.sec;
+ /* conditions to remove from cache:
+ * a) r0->expiration is set and hit
+ * -or-
+ * b) r0->session_timeout is set and hit
+ */
+ if ((!r0->expiration || expires_in > 0) &&
+ (!r0->session_timeout || session_timeout > 0)) {
+ wpa_printf(MSG_ERROR,
+ "FT: %s() called for non-expired entry %p",
+ __func__, r0);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->expiration && expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ if (r0->session_timeout && session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
+ return;
+ }
+
+ wpa_ft_free_pmk_r0(r0);
+}
+
+
+static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
+{
+ if (!r1)
+ return;
+
+ dl_list_del(&r1->list);
+ eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
+
+ os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
+ os_free(r1->vlan);
+ os_free(r1->identity);
+ os_free(r1->radius_cui);
+ os_free(r1);
+}
+
+
+static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
+
+ wpa_ft_free_pmk_r1(r1);
+}
+
+
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
{
struct wpa_ft_pmk_cache *cache;
cache = os_zalloc(sizeof(*cache));
+ if (cache) {
+ dl_list_init(&cache->pmk_r0);
+ dl_list_init(&cache->pmk_r1);
+ }
return cache;
}
@@ -181,21 +1246,13 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
struct wpa_ft_pmk_r0_sa *r0, *r0prev;
struct wpa_ft_pmk_r1_sa *r1, *r1prev;
- r0 = cache->pmk_r0;
- while (r0) {
- r0prev = r0;
- r0 = r0->next;
- os_memset(r0prev->pmk_r0, 0, PMK_LEN);
- os_free(r0prev);
- }
+ dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
+ struct wpa_ft_pmk_r0_sa, list)
+ wpa_ft_free_pmk_r0(r0);
- r1 = cache->pmk_r1;
- while (r1) {
- r1prev = r1;
- r1 = r1->next;
- os_memset(r1prev->pmk_r1, 0, PMK_LEN);
- os_free(r1prev);
- }
+ dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
+ struct wpa_ft_pmk_r1_sa, list)
+ wpa_ft_free_pmk_r1(r1);
os_free(cache);
}
@@ -203,24 +1260,63 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r0,
- const u8 *pmk_r0_name, int pairwise)
+ size_t pmk_r0_len,
+ const u8 *pmk_r0_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
- /* TODO: add expiration and limit on number of entries in cache */
+ /* TODO: add limit on number of entries in cache */
+ os_get_reltime(&now);
r0 = os_zalloc(sizeof(*r0));
if (r0 == NULL)
return -1;
- os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
+ os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
+ r0->pmk_r0_len = pmk_r0_len;
os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
os_memcpy(r0->spa, spa, ETH_ALEN);
r0->pairwise = pairwise;
+ if (expires_in > 0)
+ r0->expiration = now.sec + expires_in;
+ if (vlan && vlan->notempty) {
+ r0->vlan = os_zalloc(sizeof(*vlan));
+ if (!r0->vlan) {
+ bin_clear_free(r0, sizeof(*r0));
+ return -1;
+ }
+ *r0->vlan = *vlan;
+ }
+ if (identity) {
+ r0->identity = os_malloc(identity_len);
+ if (r0->identity) {
+ os_memcpy(r0->identity, identity, identity_len);
+ r0->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r0->radius_cui = os_malloc(radius_cui_len);
+ if (r0->radius_cui) {
+ os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
+ r0->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r0->session_timeout = now.sec + session_timeout;
- r0->next = cache->pmk_r0;
- cache->pmk_r0 = r0;
+ dl_list_add(&cache->pmk_r0, &r0->list);
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
+ r0, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r0, r0, NULL);
return 0;
}
@@ -228,49 +1324,89 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r0_name,
- u8 *pmk_r0, int *pairwise)
+ const struct wpa_ft_pmk_r0_sa **r0_out)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
+ struct os_reltime now;
- r0 = cache->pmk_r0;
- while (r0) {
+ os_get_reltime(&now);
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
- os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
- if (pairwise)
- *pairwise = r0->pairwise;
+ *r0_out = r0;
return 0;
}
-
- r0 = r0->next;
}
+ *r0_out = NULL;
return -1;
}
static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1,
- const u8 *pmk_r1_name, int pairwise)
+ size_t pmk_r1_len,
+ const u8 *pmk_r1_name, int pairwise,
+ const struct vlan_description *vlan,
+ int expires_in, int session_timeout,
+ const u8 *identity, size_t identity_len,
+ const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
- /* TODO: add expiration and limit on number of entries in cache */
+ /* TODO: limit on number of entries in cache */
+ os_get_reltime(&now);
+
+ if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
+ expires_in = max_expires_in;
r1 = os_zalloc(sizeof(*r1));
if (r1 == NULL)
return -1;
- os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
+ os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
+ r1->pmk_r1_len = pmk_r1_len;
os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
os_memcpy(r1->spa, spa, ETH_ALEN);
r1->pairwise = pairwise;
+ if (vlan && vlan->notempty) {
+ r1->vlan = os_zalloc(sizeof(*vlan));
+ if (!r1->vlan) {
+ bin_clear_free(r1, sizeof(*r1));
+ return -1;
+ }
+ *r1->vlan = *vlan;
+ }
+ if (identity) {
+ r1->identity = os_malloc(identity_len);
+ if (r1->identity) {
+ os_memcpy(r1->identity, identity, identity_len);
+ r1->identity_len = identity_len;
+ }
+ }
+ if (radius_cui) {
+ r1->radius_cui = os_malloc(radius_cui_len);
+ if (r1->radius_cui) {
+ os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
+ r1->radius_cui_len = radius_cui_len;
+ }
+ }
+ if (session_timeout > 0)
+ r1->session_timeout = now.sec + session_timeout;
- r1->next = cache->pmk_r1;
- cache->pmk_r1 = r1;
+ dl_list_add(&cache->pmk_r1, &r1->list);
+
+ if (expires_in > 0)
+ eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
+ r1, NULL);
+ if (session_timeout > 0)
+ eloop_register_timeout(session_timeout + 1, 0,
+ wpa_ft_expire_pmk_r1, r1, NULL);
return 0;
}
@@ -278,94 +1414,616 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1_name,
- u8 *pmk_r1, int *pairwise)
+ u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui, size_t *radius_cui_len,
+ int *session_timeout)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r1_sa *r1;
+ struct os_reltime now;
- r1 = cache->pmk_r1;
- while (r1) {
+ os_get_reltime(&now);
+
+ dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
- os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
+ os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
+ *pmk_r1_len = r1->pmk_r1_len;
if (pairwise)
*pairwise = r1->pairwise;
+ if (vlan && r1->vlan)
+ *vlan = *r1->vlan;
+ if (vlan && !r1->vlan)
+ os_memset(vlan, 0, sizeof(*vlan));
+ if (identity && identity_len) {
+ *identity = r1->identity;
+ *identity_len = r1->identity_len;
+ }
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r1->radius_cui;
+ *radius_cui_len = r1->radius_cui_len;
+ }
+ if (session_timeout && r1->session_timeout > now.sec)
+ *session_timeout = r1->session_timeout -
+ now.sec;
+ else if (session_timeout && r1->session_timeout)
+ *session_timeout = 1;
+ else if (session_timeout)
+ *session_timeout = 0;
return 0;
}
-
- r1 = r1->next;
}
return -1;
}
+static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
+{
+ if (r0kh->seq)
+ return 0;
+
+ r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
+ if (!r0kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r0kh->seq->rx.queue);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len,
+ struct ft_remote_r0kh **r0kh_out,
+ struct ft_remote_r0kh **r0kh_wildcard)
+{
+ struct ft_remote_r0kh *r0kh;
+
+ *r0kh_wildcard = NULL;
+ *r0kh_out = NULL;
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ for (; r0kh; r0kh = r0kh->next) {
+ if (r0kh->id_len == 1 && r0kh->id[0] == '*')
+ *r0kh_wildcard = r0kh;
+ if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
+ os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
+ *r0kh_out = r0kh;
+ }
+
+ if (!*r0kh_out && !*r0kh_wildcard)
+ wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
+
+ if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
+ *r0kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
+{
+ if (r1kh->seq)
+ return 0;
+
+ r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
+ if (!r1kh->seq) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
+ return -1;
+ }
+
+ dl_list_init(&r1kh->seq->rx.queue);
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r1kh_id,
+ struct ft_remote_r1kh **r1kh_out,
+ struct ft_remote_r1kh **r1kh_wildcard)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ *r1kh_wildcard = NULL;
+ *r1kh_out = NULL;
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ for (; r1kh; r1kh = r1kh->next) {
+ if (is_zero_ether_addr(r1kh->addr) &&
+ is_zero_ether_addr(r1kh->id))
+ *r1kh_wildcard = r1kh;
+ if (f_r1kh_id &&
+ os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
+ *r1kh_out = r1kh;
+ }
+
+ if (!*r1kh_out && !*r1kh_wildcard)
+ wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
+
+ if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
+ *r1kh_out = NULL;
+}
+
+
+static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+ if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
+ os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
+ f_r0kh_id_len) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r1kh_id)
+{
+ if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct ft_remote_r0kh *r0kh, *prev = NULL;
+
+ if (!wpa_auth->conf.r0kh_list)
+ return;
+
+ for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
+ if (r0kh == timeout_ctx)
+ break;
+ prev = r0kh;
+ }
+ if (!r0kh)
+ return;
+ if (prev)
+ prev->next = r0kh->next;
+ else
+ *wpa_auth->conf.r0kh_list = r0kh->next;
+ if (r0kh->seq)
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ os_free(r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh, int timeout)
+{
+ if (timeout > 0)
+ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+}
+
+
+static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh, int timeout)
+{
+ eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+}
+
+
+static struct ft_remote_r0kh *
+wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r0kh *r0kh_wildcard,
+ const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
+ int timeout)
+{
+ struct ft_remote_r0kh *r0kh;
+
+ if (!wpa_auth->conf.r0kh_list)
+ return NULL;
+
+ r0kh = os_zalloc(sizeof(*r0kh));
+ if (!r0kh)
+ return NULL;
+
+ if (src_addr)
+ os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
+
+ if (id_len > FT_R0KH_ID_MAX_LEN)
+ id_len = FT_R0KH_ID_MAX_LEN;
+ os_memcpy(r0kh->id, r0kh_id, id_len);
+ r0kh->id_len = id_len;
+
+ os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
+
+ r0kh->next = *wpa_auth->conf.r0kh_list;
+ *wpa_auth->conf.r0kh_list = r0kh;
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
+ wpa_auth, r0kh);
+
+ if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
+ return NULL;
+
+ return r0kh;
+}
+
+
+static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct ft_remote_r1kh *r1kh, *prev = NULL;
+
+ if (!wpa_auth->conf.r1kh_list)
+ return;
+
+ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (r1kh == timeout_ctx)
+ break;
+ prev = r1kh;
+ }
+ if (!r1kh)
+ return;
+ if (prev)
+ prev->next = r1kh->next;
+ else
+ *wpa_auth->conf.r1kh_list = r1kh->next;
+ if (r1kh->seq)
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ os_free(r1kh);
+}
+
+
+static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r1kh *r1kh, int timeout)
+{
+ if (timeout > 0)
+ eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+ wpa_auth, r1kh);
+}
+
+
+static struct ft_remote_r1kh *
+wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
+ struct ft_remote_r1kh *r1kh_wildcard,
+ const u8 *src_addr, const u8 *r1kh_id, int timeout)
+{
+ struct ft_remote_r1kh *r1kh;
+
+ if (!wpa_auth->conf.r1kh_list)
+ return NULL;
+
+ r1kh = os_zalloc(sizeof(*r1kh));
+ if (!r1kh)
+ return NULL;
+
+ os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
+ os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
+ os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
+ r1kh->next = *wpa_auth->conf.r1kh_list;
+ *wpa_auth->conf.r1kh_list = r1kh;
+
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
+ wpa_auth, r1kh);
+
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ return NULL;
+
+ return r1kh;
+}
+
+
+void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
+{
+ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+}
+
+
+static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh;
+ struct ft_remote_r1kh *r1kh;
+
+ eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ for (; r0kh; r0kh = r0kh->next) {
+ if (!r0kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
+ os_free(r0kh->seq);
+ r0kh->seq = NULL;
+ }
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ for (; r1kh; r1kh = r1kh->next) {
+ if (!r1kh->seq)
+ continue;
+ wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
+ os_free(r1kh->seq);
+ r1kh->seq = NULL;
+ }
+}
+
+
+static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
+ struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
+
+ if (wpa_auth->conf.r0kh_list)
+ r0kh = *wpa_auth->conf.r0kh_list;
+ else
+ r0kh = NULL;
+ while (r0kh) {
+ r0kh_next = r0kh->next;
+ if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
+ r0kh) > 0) {
+ if (r0kh_prev)
+ r0kh_prev->next = r0kh_next;
+ else
+ *wpa_auth->conf.r0kh_list = r0kh_next;
+ os_free(r0kh);
+ } else {
+ r0kh_prev = r0kh;
+ }
+ r0kh = r0kh_next;
+ }
+
+ if (wpa_auth->conf.r1kh_list)
+ r1kh = *wpa_auth->conf.r1kh_list;
+ else
+ r1kh = NULL;
+ while (r1kh) {
+ r1kh_next = r1kh->next;
+ if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
+ r1kh) > 0) {
+ if (r1kh_prev)
+ r1kh_prev->next = r1kh_next;
+ else
+ *wpa_auth->conf.r1kh_list = r1kh_next;
+ os_free(r1kh);
+ } else {
+ r1kh_prev = r1kh;
+ }
+ r1kh = r1kh_next;
+ }
+}
+
+
+void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
+{
+ wpa_ft_deinit_seq(wpa_auth);
+ wpa_ft_deinit_rkh_tmp(wpa_auth);
+}
+
+
+static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
+ const u8 *f_r0kh_id, size_t f_r0kh_id_len)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+
+ if (!wpa_auth->conf.rkh_neg_timeout)
+ return;
+
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+
+ if (!r0kh_wildcard) {
+ /* r0kh removed after neg_timeout and might need re-adding */
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_neg_timeout);
+ os_memset(r0kh->addr, 0, ETH_ALEN);
+ } else
+ wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
+ f_r0kh_id_len,
+ wpa_auth->conf.rkh_neg_timeout);
+}
+
+
+static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
+ MAC2STR(sm->addr));
+ if (sm->ft_pending_pull_left_retries <= 0)
+ wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
+
+ /* cancel multiple timeouts */
+ eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
+ ft_finish_pull(sm);
+}
+
+
static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
const u8 *pmk_r0_name)
{
- struct ft_remote_r0kh *r0kh;
- struct ft_r0kh_r1kh_pull_frame frame, f;
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+ u8 *packet = NULL;
+ const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
+ size_t packet_len, key_len;
+ struct ft_rrb_seq f_seq;
+ int tsecs, tusecs, first;
+ struct wpabuf *ft_pending_req_ies;
+ int r0kh_timeout;
+ struct tlv_list req_enc[] = {
+ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+ .data = pmk_r0_name },
+ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+ .data = sm->addr },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+ struct tlv_list req_auth[] = {
+ { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
+ .data = sm->ft_pending_pull_nonce },
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
+ { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
+ .data = sm->r0kh_id },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = f_r1kh_id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
- r0kh = sm->wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (r0kh->id_len == sm->r0kh_id_len &&
- os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
- 0)
- break;
- r0kh = r0kh->next;
+ if (sm->ft_pending_pull_left_retries <= 0)
+ return -1;
+ first = sm->ft_pending_pull_left_retries ==
+ sm->wpa_auth->conf.rkh_pull_retries;
+ sm->ft_pending_pull_left_retries--;
+
+ wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+
+ /* Keep r0kh sufficiently long in the list for seq num check */
+ r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
+ 1 + ftRRBseqTimeout;
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
+ } else if (r0kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+ /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
+ r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
+ r0kh_wildcard->addr,
+ sm->r0kh_id, sm->r0kh_id_len,
+ r0kh_timeout);
}
if (r0kh == NULL) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
+ if (is_zero_ether_addr(r0kh->addr)) {
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+ if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: R0KH-ID points to self - no matching key available");
+ return -1;
+ }
+
+ key = r0kh->key;
+ key_len = sizeof(r0kh->key);
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
"address " MACSTR, MAC2STR(r0kh->addr));
- os_memset(&frame, 0, sizeof(frame));
- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
- frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
- frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
- os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
+ if (r0kh->seq->rx.num_last == 0) {
+ /* A sequence request will be sent out anyway when pull
+ * response is received. Send it out now to avoid one RTT. */
+ wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
+ r0kh->id, r0kh->id_len, f_r1kh_id, key,
+ key_len, NULL, 0, NULL, 0, NULL);
+ }
- /* aes_wrap() does not support inplace encryption, so use a temporary
- * buffer for the data. */
- if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
+ if (first &&
+ random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
- os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
- FT_R0KH_R1KH_PULL_NONCE_LEN);
- os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
- os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
- os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
- os_memset(f.pad, 0, sizeof(f.pad));
- if (aes_wrap(r0kh->key, sizeof(r0kh->key),
- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
- f.nonce, frame.nonce) < 0)
+ if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
+ sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
+ &packet, &packet_len) < 0)
return -1;
+ ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
wpabuf_free(sm->ft_pending_req_ies);
- sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
- if (sm->ft_pending_req_ies == NULL)
+ sm->ft_pending_req_ies = ft_pending_req_ies;
+ if (!sm->ft_pending_req_ies) {
+ os_free(packet);
return -1;
+ }
- wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+ tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
+ tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
+ eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
+
+ wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
+ packet, packet_len);
+
+ os_free(packet);
return 0;
}
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
+ const u8 *pmk_r0, const u8 *pmk_r0_name)
+{
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
+ size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
+ SHA384_MAC_LEN : PMK_LEN;
+
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
+
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+ return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name, sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+}
+
+
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
struct wpa_ptk *ptk)
{
- u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
- u8 pmk_r1[PMK_LEN];
+ 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) ?
+ SHA384_MAC_LEN : PMK_LEN;
+ size_t pmk_r1_len = pmk_r0_len;
+ u8 pmk_r1[PMK_LEN_MAX];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
@@ -373,6 +2031,12 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
const u8 *ssid = sm->wpa_auth->conf.ssid;
size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+ int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
+ int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len, radius_cui_len;
+ int session_timeout;
if (sm->xxkey_len == 0) {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -380,23 +2044,45 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
return -1;
}
- wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
- r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
- sm->pairwise);
+ if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
+ MAC2STR(sm->addr));
+ return -1;
+ }
- wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
- pmk_r1, sm->pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+ identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
+ radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
+ &radius_cui);
+ session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
+
+ if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+ r0kh, r0kh_len, sm->addr,
+ pmk_r0, pmk_r0_name,
+ wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
+ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+ wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
+ pmk_r0_name,
+ sm->pairwise, &vlan, expires_in,
+ session_timeout, identity, identity_len,
+ radius_cui, radius_cui_len);
+
+ if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
+ pmk_r1, sm->pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
- wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
- sm->pairwise);
+ if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+ wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
+ sm->pmk_r1_name, sm->pairwise, &vlan,
+ expires_in, session_timeout, identity,
+ identity_len, radius_cui, radius_cui_len);
- return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, sm->pmk_r1_name,
+ return wpa_pmk_r1_to_ptk(pmk_r1, 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);
}
@@ -404,9 +2090,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
- if (wpa_auth->cb.get_seqnum == NULL)
+ if (wpa_auth->cb->get_seqnum == NULL)
return -1;
- return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+ return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
}
@@ -418,6 +2104,16 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
const u8 *key;
size_t key_len;
u8 keybuf[32];
+ const u8 *kek;
+ size_t kek_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
key_len = gsm->GTK_len;
if (key_len > sizeof(keybuf))
@@ -456,8 +2152,10 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
subelem[4] = gsm->GTK_len;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
- subelem + 13)) {
+ if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: GTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
os_free(subelem);
return NULL;
}
@@ -473,10 +2171,23 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
u8 *subelem, *pos;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
+ const u8 *kek;
+ size_t kek_len;
+ size_t igtk_len;
+
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kek = sm->PTK.kek2;
+ kek_len = sm->PTK.kek2_len;
+ } else {
+ kek = sm->PTK.kek;
+ kek_len = sm->PTK.kek_len;
+ }
+
+ igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
* Key[16+8] */
- subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
+ subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
@@ -488,9 +2199,12 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
pos += 2;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
pos += 6;
- *pos++ = WPA_IGTK_LEN;
- if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
+ *pos++ = igtk_len;
+ if (aes_wrap(kek, kek_len, igtk_len / 8,
gsm->IGTK[gsm->GN_igtk - 4], pos)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: IGTK subelem encryption failed: kek_len=%d",
+ (int) kek_len);
os_free(subelem);
return NULL;
}
@@ -637,17 +2351,21 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
const u8 *req_ies, size_t req_ies_len)
{
u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+ u8 *fte_mic, *elem_count;
size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
int res;
struct wpa_auth_config *conf;
- struct rsn_ftie *_ftie;
struct wpa_ft_ies parse;
u8 *ric_start;
u8 *anonce, *snonce;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384;
if (sm == NULL)
return pos;
+ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
conf = &sm->wpa_auth->conf;
if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
@@ -662,7 +2380,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
*/
res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
if (res < 0)
- return pos;
+ return NULL;
rsnie = pos;
rsnie_len = res;
pos += res;
@@ -671,7 +2389,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
/* Mobility Domain Information */
res = wpa_write_mdie(conf, pos, end - pos);
if (res < 0)
- return pos;
+ return NULL;
mdie = pos;
mdie_len = res;
pos += res;
@@ -679,6 +2397,11 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
/* Fast BSS Transition Information */
if (auth_alg == WLAN_AUTH_FT) {
subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
+ if (!subelem) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add GTK subelement");
+ return NULL;
+ }
r0kh_id = sm->r0kh_id;
r0kh_id_len = sm->r0kh_id_len;
anonce = sm->ANonce;
@@ -690,14 +2413,16 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
u8 *nbuf;
igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
if (igtk == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to add IGTK subelement");
os_free(subelem);
- return pos;
+ return NULL;
}
nbuf = os_realloc(subelem, subelem_len + igtk_len);
if (nbuf == NULL) {
os_free(subelem);
os_free(igtk);
- return pos;
+ return NULL;
}
subelem = nbuf;
os_memcpy(subelem + subelem_len, igtk, igtk_len);
@@ -711,44 +2436,66 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
anonce = NULL;
snonce = NULL;
}
- res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
- end - pos, subelem, subelem_len);
+ res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
+ anonce, snonce, pos, end - pos,
+ subelem, subelem_len);
os_free(subelem);
if (res < 0)
- return pos;
+ return NULL;
ftie = pos;
ftie_len = res;
pos += res;
- _ftie = (struct rsn_ftie *) (ftie + 2);
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *_ftie =
+ (struct rsn_ftie_sha384 *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ } else {
+ struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
+
+ fte_mic = _ftie->mic;
+ elem_count = &_ftie->mic_control[1];
+ }
if (auth_alg == WLAN_AUTH_FT)
- _ftie->mic_control[1] = 3; /* Information element count */
+ *elem_count = 3; /* Information element count */
ric_start = pos;
- if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+ if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
+ && parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
- _ftie->mic_control[1] +=
+ *elem_count +=
ieee802_11_ie_count(ric_start,
pos - ric_start);
}
if (ric_start == pos)
ric_start = NULL;
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
if (auth_alg == WLAN_AUTH_FT &&
- wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
- sm->wpa_auth->addr, 6,
+ wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
mdie, mdie_len, ftie, ftie_len,
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
- _ftie->mic) < 0)
+ fte_mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return NULL;
+ }
os_free(sm->assoc_resp_ftie);
sm->assoc_resp_ftie = os_malloc(ftie_len);
- if (sm->assoc_resp_ftie)
- os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+ if (!sm->assoc_resp_ftie)
+ return NULL;
+ os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
return pos;
}
@@ -759,10 +2506,10 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
enum wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len)
{
- if (wpa_auth->cb.set_key == NULL)
+ if (wpa_auth->cb->set_key == NULL)
return -1;
- return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
- key, key_len);
+ return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+ key, key_len);
}
@@ -804,20 +2551,219 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
}
+/* Derive PMK-R1 from PSK, check all available PSK */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *req_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *out_vlan,
+ const u8 **out_identity, size_t *out_identity_len,
+ const u8 **out_radius_cui,
+ size_t *out_radius_cui_len,
+ int *out_session_timeout)
+{
+ const u8 *pmk = NULL;
+ u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ const u8 *mdid = wpa_auth->conf.mobility_domain;
+ const u8 *r0kh = sm->r0kh_id;
+ size_t r0kh_len = sm->r0kh_id_len;
+ const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+ const u8 *ssid = wpa_auth->conf.ssid;
+ size_t ssid_len = wpa_auth->conf.ssid_len;
+ int pairwise;
+
+ pairwise = sm->pairwise;
+
+ for (;;) {
+ pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+ pmk);
+ if (pmk == NULL)
+ break;
+
+ if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+ r0kh_len, sm->addr,
+ pmk_r0, pmk_r0_name, 0) < 0 ||
+ wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
+ sm->addr, pmk_r1, pmk_r1_name) < 0 ||
+ os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0)
+ continue;
+
+ /* We found a PSK that matches the requested pmk_r1_name */
+ wpa_printf(MSG_DEBUG,
+ "FT: Found PSK to generate PMK-R1 locally");
+ os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+ if (out_pairwise)
+ *out_pairwise = pairwise;
+ 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 "
+ MACSTR, MAC2STR(sm->addr));
+ return -1;
+ }
+
+ if (out_identity && out_identity_len) {
+ *out_identity_len = wpa_ft_get_identity(
+ sm->wpa_auth, sm->addr, out_identity);
+ }
+
+ if (out_radius_cui && out_radius_cui_len) {
+ *out_radius_cui_len = wpa_ft_get_radius_cui(
+ sm->wpa_auth, sm->addr, out_radius_cui);
+ }
+
+ if (out_session_timeout) {
+ *out_session_timeout = wpa_ft_get_session_timeout(
+ sm->wpa_auth, sm->addr);
+ }
+
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not find PSK to generate PMK-R1 locally");
+ return -1;
+}
+
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
+ struct wpa_ft_ies *parse)
+{
+ int key_mgmt, ciphers;
+
+ if (sm->wpa_key_mgmt)
+ return 0;
+
+ key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+ if (!key_mgmt) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
+ MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+ return -1;
+ }
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
+ else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+#ifdef CONFIG_FILS
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_FILS */
+ ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
+ if (!ciphers) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
+ MACSTR,
+ parse->pairwise_cipher, MAC2STR(sm->addr));
+ return -1;
+ }
+ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+ return 0;
+}
+
+
+static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *req_pmk_r0_name,
+ const u8 *req_pmk_r1_name,
+ u8 *out_pmk_r1, int *out_pairwise,
+ struct vlan_description *vlan,
+ const u8 **identity, size_t *identity_len,
+ const u8 **radius_cui,
+ size_t *radius_cui_len,
+ int *out_session_timeout)
+{
+ struct wpa_auth_config *conf = &wpa_auth->conf;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ int expires_in = 0;
+ int session_timeout = 0;
+ struct os_reltime now;
+
+ if (conf->r0_key_holder_len != r0kh_id_len ||
+ os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
+ 0)
+ return -1; /* not our R0KH-ID */
+
+ wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
+ if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
+ 0)
+ return -1; /* no matching PMKR0Name in local cache */
+
+ wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
+
+ if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
+ conf->r1_key_holder,
+ sm->addr, out_pmk_r1, pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ os_get_reltime(&now);
+ if (r0->expiration)
+ expires_in = r0->expiration - now.sec;
+
+ if (r0->session_timeout)
+ session_timeout = r0->session_timeout - now.sec;
+
+ wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
+ pmk_r1_name,
+ sm->pairwise, r0->vlan, expires_in, session_timeout,
+ r0->identity, r0->identity_len,
+ r0->radius_cui, r0->radius_cui_len);
+
+ *out_pairwise = sm->pairwise;
+ if (vlan) {
+ if (r0->vlan)
+ *vlan = *r0->vlan;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+ }
+
+ if (identity && identity_len) {
+ *identity = r0->identity;
+ *identity_len = r0->identity_len;
+ }
+
+ if (radius_cui && radius_cui_len) {
+ *radius_cui = r0->radius_cui;
+ *radius_cui_len = r0->radius_cui_len;
+ }
+
+ *out_session_timeout = session_timeout;
+
+ return 0;
+}
+
+
static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
u8 **resp_ies, size_t *resp_ies_len)
{
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
- u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
u8 ptk_name[WPA_PMK_NAME_LEN];
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
size_t buflen;
int ret;
u8 *pos, *end;
- int pairwise;
+ int pairwise, session_timeout = 0;
+ struct vlan_description vlan;
+ const u8 *identity, *radius_cui;
+ size_t identity_len = 0, radius_cui_len = 0;
+ int use_sha384;
+ size_t pmk_r1_len;
*resp_ies = NULL;
*resp_ies_len = 0;
@@ -828,10 +2774,12 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
+ pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
@@ -842,13 +2790,27 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
return WLAN_STATUS_INVALID_MDIE;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
- return WLAN_STATUS_INVALID_FTIE;
- }
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
- os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (!ftie || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
+ }
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
@@ -865,26 +2827,58 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
return WLAN_STATUS_INVALID_PMKID;
}
+ if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
- wpa_derive_pmk_r1_name(parse.rsn_pmkid,
- sm->wpa_auth->conf.r1_key_holder, sm->addr,
- pmk_r1_name);
+ if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+ sm->wpa_auth->conf.r1_key_holder, sm->addr,
+ pmk_r1_name, use_sha384) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
pmk_r1_name, WPA_PMK_NAME_LEN);
- if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
- &pairwise) < 0) {
+ if (conf->ft_psk_generate_local &&
+ wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+ if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) < 0)
+ return WLAN_STATUS_INVALID_PMKID;
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 for FT-PSK locally");
+ } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+ pmk_r1, &pmk_r1_len, &pairwise, &vlan,
+ &identity, &identity_len, &radius_cui,
+ &radius_cui_len, &session_timeout) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
+ if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
+ parse.r0kh_id, parse.r0kh_id_len,
+ parse.rsn_pmkid,
+ pmk_r1_name, pmk_r1, &pairwise,
+ &vlan, &identity, &identity_len,
+ &radius_cui, &radius_cui_len,
+ &session_timeout) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Generated PMK-R1 based on local PMK-R0");
+ goto pmk_r1_derived;
+ }
+
if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Did not have matching "
- "PMK-R1 and unknown R0KH-ID");
+ wpa_printf(MSG_DEBUG,
+ "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
return WLAN_STATUS_INVALID_PMKID;
}
return -1; /* Status pending */
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
}
- wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
+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);
@@ -899,8 +2893,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
- if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, pmk_r1_name,
+ if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr, pmk_r1_name,
&sm->PTK, ptk_name, sm->wpa_key_mgmt,
pairwise) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -910,44 +2904,51 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
sm->tk_already_set = FALSE;
wpa_ft_install_ptk(sm);
+ if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
+ identity, identity_len) < 0 ||
+ wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
+ radius_cui, radius_cui_len) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
+
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
2 + FT_R1KH_ID_LEN + 200;
*resp_ies = os_zalloc(buflen);
- if (*resp_ies == NULL) {
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (*resp_ies == NULL)
+ goto fail;
pos = *resp_ies;
end = *resp_ies + buflen;
ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
- if (ret < 0) {
- os_free(*resp_ies);
- *resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (ret < 0)
+ goto fail;
pos += ret;
ret = wpa_write_mdie(conf, pos, end - pos);
- if (ret < 0) {
- os_free(*resp_ies);
- *resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (ret < 0)
+ goto fail;
pos += ret;
- ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
+ ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
- if (ret < 0) {
- os_free(*resp_ies);
- *resp_ies = NULL;
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
+ if (ret < 0)
+ goto fail;
pos += ret;
*resp_ies_len = pos - *resp_ies;
return WLAN_STATUS_SUCCESS;
+fail:
+ os_free(*resp_ies);
+ *resp_ies = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -975,6 +2976,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
sm->ft_pending_cb = cb;
sm->ft_pending_cb_ctx = ctx;
sm->ft_pending_auth_transaction = auth_transaction;
+ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
&resp_ies_len);
if (res < 0) {
@@ -998,17 +3000,23 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
- struct rsn_ftie *ftie;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
size_t mic_len = 16;
unsigned int count;
+ const u8 *kck;
+ size_t kck_len;
+ int use_sha384;
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
+
wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
- if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -1039,34 +3047,56 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_INVALID_MDIE;
}
- ftie = (struct rsn_ftie *) parse.ftie;
- if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
- wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *ftie;
+
+ ftie = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
+ } else {
+ struct rsn_ftie *ftie;
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return WLAN_STATUS_INVALID_FTIE;
+ }
+
+ anonce = ftie->anonce;
+ snonce = ftie->snonce;
+ fte_elem_count = ftie->mic_control[1];
+ fte_mic = ftie->mic;
+ }
+
+ if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sm->SNonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
- if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
- wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
- ftie->snonce, WPA_NONCE_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
- sm->SNonce, WPA_NONCE_LEN);
- return -1;
- }
-
- if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+ if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
- ftie->anonce, WPA_NONCE_LEN);
+ anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -1078,12 +3108,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
@@ -1094,7 +3124,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
- return -1;
+ return WLAN_STATUS_INVALID_FTIE;
}
if (parse.rsn_pmkid == NULL ||
@@ -1102,21 +3132,27 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
"RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
- return -1;
+ return WLAN_STATUS_INVALID_PMKID;
}
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
- if (ftie->mic_control[1] != count) {
+ if (fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
- ftie->mic_control[1], count);
- return -1;
+ fte_elem_count, count);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
- sm->wpa_auth->addr, 5,
+ if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ kck = sm->PTK.kck2;
+ kck_len = sm->PTK.kck2_len;
+ } else {
+ kck = sm->PTK.kck;
+ kck_len = sm->PTK.kck_len;
+ }
+ if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -1126,12 +3162,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
+ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
- ftie->mic, mic_len);
+ fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
parse.mdie - 2, parse.mdie_len + 2);
@@ -1199,6 +3235,11 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
+ if (!sm->wpa_auth->conf.ft_over_ds) {
+ wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
+ return -1;
+ }
+
/* RRB - Forward action frame to the target AP */
frame = os_malloc(sizeof(*frame) + len);
if (frame == NULL)
@@ -1251,6 +3292,7 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
sm->ft_pending_cb_ctx = sm;
os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+ sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
&resp_ies_len);
if (res < 0) {
@@ -1316,112 +3358,431 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
}
+static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
+ const struct tlv_list *tlvs,
+ const struct wpa_ft_pmk_r0_sa *pmk_r0,
+ const u8 *r1kh_id, const u8 *s1kh_id,
+ const struct tlv_list *tlv_auth,
+ const u8 *src_addr, u8 type,
+ u8 **packet, size_t *packet_len)
+{
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len = pmk_r0->pmk_r0_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 f_pairwise[sizeof(le16)];
+ u8 f_expires_in[sizeof(le16)];
+ u8 f_session_timeout[sizeof(le32)];
+ int expires_in;
+ int session_timeout;
+ struct os_reltime now;
+ int ret;
+ struct tlv_list sess_tlv[] = {
+ { .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
+ .data = pmk_r1 },
+ { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
+ .data = pmk_r1_name },
+ { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
+ .data = f_pairwise },
+ { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
+ .data = f_expires_in },
+ { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
+ .data = pmk_r0->identity },
+ { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
+ .data = pmk_r0->radius_cui },
+ { .type = FT_RRB_SESSION_TIMEOUT,
+ .len = sizeof(f_session_timeout),
+ .data = f_session_timeout },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
+ pmk_r0->pmk_r0_name, r1kh_id,
+ s1kh_id, pmk_r1, pmk_r1_name) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)",
+ pmk_r1, pmk_r1_len);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)",
+ pmk_r1_name, WPA_PMK_NAME_LEN);
+ WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
+
+ os_get_reltime(&now);
+ if (pmk_r0->expiration > now.sec)
+ expires_in = pmk_r0->expiration - now.sec;
+ else if (pmk_r0->expiration)
+ expires_in = 1;
+ else
+ expires_in = 0;
+ WPA_PUT_LE16(f_expires_in, expires_in);
+
+ if (pmk_r0->session_timeout > now.sec)
+ session_timeout = pmk_r0->session_timeout - now.sec;
+ else if (pmk_r0->session_timeout)
+ session_timeout = 1;
+ else
+ session_timeout = 0;
+ WPA_PUT_LE32(f_session_timeout, session_timeout);
+
+ ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
+ pmk_r0->vlan, src_addr, type,
+ packet, packet_len);
+
+ os_memset(pmk_r1, 0, sizeof(pmk_r1));
+
+ return ret;
+}
+
+
static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
- const u8 *data, size_t data_len)
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
{
- struct ft_r0kh_r1kh_pull_frame f;
- const u8 *crypt;
- u8 *plain;
- struct ft_remote_r1kh *r1kh;
- struct ft_r0kh_r1kh_resp_frame resp, r;
- u8 pmk_r0[PMK_LEN];
- int pairwise;
+ const char *msgtype = "pull request";
+ u8 *plain = NULL, *packet = NULL;
+ size_t plain_len = 0, packet_len = 0;
+ struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
+ const u8 *key;
+ size_t key_len;
+ int seq_ret;
+ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
+ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
+ size_t f_pmk_r0_name_len;
+ const struct wpa_ft_pmk_r0_sa *r0;
+ int ret;
+ struct tlv_list resp[2];
+ struct tlv_list resp_auth[5];
+ struct ft_rrb_seq f_seq;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
- if (data_len < sizeof(f))
- return -1;
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
- r1kh = wpa_auth->conf.r1kh_list;
- while (r1kh) {
- if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r1kh = r1kh->next;
- }
- if (r1kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
- "PMK-R1 pull source address " MACSTR,
- MAC2STR(src_addr));
- return -1;
+ if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
+ goto out;
}
- crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
- os_memset(&f, 0, sizeof(f));
- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
- /* aes_unwrap() does not support inplace decryption, so use a temporary
- * buffer for the data. */
- if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
- (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
- crypt, plain) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
- "request from " MACSTR, MAC2STR(src_addr));
- return -1;
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
+ if (r1kh) {
+ key = r1kh->key;
+ key_len = sizeof(r1kh->key);
+ } else if (r1kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
+ key = r1kh_wildcard->key;
+ key_len = sizeof(r1kh_wildcard->key);
+ } else {
+ goto out;
}
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
- f.nonce, sizeof(f.nonce));
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
- f.pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
- MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
- os_memset(&resp, 0, sizeof(resp));
- resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
- resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
- resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
- os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
-
- /* aes_wrap() does not support inplace encryption, so use a temporary
- * buffer for the data. */
- os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
- os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
- os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
- if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
- &pairwise) < 0) {
- wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
- "PMK-R1 pull");
- return -1;
+ seq_ret = FT_RRB_SEQ_DROP;
+ if (r1kh)
+ seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
+ auth, auth_len, msgtype, no_defer);
+ if (!no_defer && r1kh_wildcard &&
+ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ /* wildcard: r1kh-id unknown or changed addr -> do a seq req */
+ seq_ret = FT_RRB_SEQ_DEFER;
}
- wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
- r.pmk_r1, r.pmk_r1_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
- WPA_PMK_NAME_LEN);
- r.pairwise = host_to_le16(pairwise);
- os_memset(r.pad, 0, sizeof(r.pad));
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
- r.nonce, resp.nonce) < 0) {
- os_memset(pmk_r0, 0, PMK_LEN);
- return -1;
+ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+ src_addr, FT_PACKET_R0KH_R1KH_PULL,
+ &plain, &plain_len) < 0)
+ goto out;
+
+ if (!r1kh)
+ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
+ f_r1kh_id,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (!r1kh)
+ goto out;
+
+ if (seq_ret == FT_RRB_SEQ_DEFER) {
+ wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
+ f_r0kh_id_len, f_r1kh_id, key, key_len,
+ enc, enc_len, auth, auth_len,
+ &wpa_ft_rrb_rx_pull);
+ goto out;
}
- os_memset(pmk_r0, 0, PMK_LEN);
+ wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
+ msgtype);
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+ wpa_auth->conf.rkh_pos_timeout);
- wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
+ RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
+ f_pmk_r0_name_len);
+
+ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
+ resp[0].type = FT_RRB_S1KH_ID;
+ resp[0].len = f_s1kh_id_len;
+ resp[0].data = f_s1kh_id;
+ resp[1].type = FT_RRB_LAST_EMPTY;
+ resp[1].len = 0;
+ resp[1].data = NULL;
+
+ resp_auth[0].type = FT_RRB_NONCE;
+ resp_auth[0].len = f_nonce_len;
+ resp_auth[0].data = f_nonce;
+ resp_auth[1].type = FT_RRB_SEQ;
+ resp_auth[1].len = sizeof(f_seq);
+ resp_auth[1].data = (u8 *) &f_seq;
+ resp_auth[2].type = FT_RRB_R0KH_ID;
+ resp_auth[2].len = f_r0kh_id_len;
+ resp_auth[2].data = f_r0kh_id;
+ resp_auth[3].type = FT_RRB_R1KH_ID;
+ resp_auth[3].len = f_r1kh_id_len;
+ resp_auth[3].data = f_r1kh_id;
+ resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ resp_auth[4].len = 0;
+ resp_auth[4].data = NULL;
+
+ if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
+ ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
+ NULL, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ } else {
+ ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
+ f_s1kh_id, resp_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_RESP,
+ &packet, &packet_len);
+ }
+
+ if (!ret)
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_RESP, packet,
+ packet_len);
+
+out:
+ os_free(plain);
+ os_free(packet);
return 0;
}
-static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+/* @returns 0 on success
+ * -1 on error
+ * -2 if FR_RRB_PAIRWISE is missing
+ */
+static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, u8 type,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ const char *msgtype, u8 *s1kh_id_out,
+ int (*cb)(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer))
+{
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+ struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
+ const u8 *key;
+ size_t key_len;
+ int seq_ret;
+ const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
+ const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
+ const u8 *f_expires_in;
+ size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
+ const u8 *f_identity, *f_radius_cui;
+ const u8 *f_session_timeout;
+ size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
+ size_t f_expires_in_len;
+ size_t f_identity_len, f_radius_cui_len;
+ size_t f_session_timeout_len;
+ int pairwise;
+ int ret = -1;
+ int expires_in;
+ int session_timeout;
+ struct vlan_description vlan;
+ size_t pmk_r1_len;
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
+
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
+
+ if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
+ wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
+ goto out;
+ }
+
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
+ &r0kh_wildcard);
+ if (r0kh) {
+ key = r0kh->key;
+ key_len = sizeof(r0kh->key);
+ } else if (r0kh_wildcard) {
+ wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
+ key = r0kh_wildcard->key;
+ key_len = sizeof(r0kh_wildcard->key);
+ } else {
+ goto out;
+ }
+
+ seq_ret = FT_RRB_SEQ_DROP;
+ if (r0kh) {
+ seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
+ auth, auth_len, msgtype,
+ cb ? 0 : 1);
+ }
+ if (cb && r0kh_wildcard &&
+ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ /* wildcard: r0kh-id unknown or changed addr -> do a seq req */
+ seq_ret = FT_RRB_SEQ_DEFER;
+ }
+
+ if (seq_ret == FT_RRB_SEQ_DROP)
+ goto out;
+
+ if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ if (!r0kh)
+ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
+ f_r0kh_id, f_r0kh_id_len,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (!r0kh)
+ goto out;
+
+ if (seq_ret == FT_RRB_SEQ_DEFER) {
+ wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
+ f_r0kh_id_len, f_r1kh_id, key, key_len,
+ enc, enc_len, auth, auth_len, cb);
+ goto out;
+ }
+
+ wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
+ msgtype);
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_pos_timeout);
+
+ RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
+
+ if (s1kh_id_out)
+ os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
+
+ ret = -2;
+ RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
+ wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
+
+ ret = -1;
+ RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+ f_pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ pmk_r1_len = PMK_LEN;
+ if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
+ &f_pmk_r1) == 0 &&
+ (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
+ pmk_r1_len = f_pmk_r1_len;
+ RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
+
+ pairwise = WPA_GET_LE16(f_pairwise);
+
+ RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
+ sizeof(le16));
+ if (f_expires_in)
+ expires_in = WPA_GET_LE16(f_expires_in);
+ else
+ expires_in = 0;
+
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
+ expires_in);
+
+ if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
+ wpa_ft_rrb_dump(plain, plain_len);
+ goto out;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
+ le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
+
+ RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
+ if (f_identity)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
+ f_identity_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
+ if (f_radius_cui)
+ wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
+ f_radius_cui_len);
+
+ RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
+ sizeof(le32));
+ if (f_session_timeout)
+ session_timeout = WPA_GET_LE32(f_session_timeout);
+ else
+ session_timeout = 0;
+ wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
+
+ if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
+ f_pmk_r1_name,
+ pairwise, &vlan, expires_in, session_timeout,
+ f_identity, f_identity_len, f_radius_cui,
+ f_radius_cui_len) < 0)
+ goto out;
+
+ ret = 0;
+out:
+ if (plain) {
+ os_memset(plain, 0, plain_len);
+ os_free(plain);
+ }
+
+ return ret;
+
+}
+
+
+static void ft_finish_pull(struct wpa_state_machine *sm)
{
- struct wpa_state_machine *sm = eloop_ctx;
int res;
u8 *resp_ies;
size_t resp_ies_len;
u16 status;
+ if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
+ return;
+
res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
wpabuf_len(sm->ft_pending_req_ies),
&resp_ies, &resp_ies_len);
+ if (res < 0) {
+ /* this loop is broken by ft_pending_pull_left_retries */
+ wpa_printf(MSG_DEBUG,
+ "FT: Callback postponed until response is available");
+ return;
+ }
wpabuf_free(sm->ft_pending_req_ies);
sm->ft_pending_req_ies = NULL;
- if (res < 0)
- res = WLAN_STATUS_UNSPECIFIED_FAILURE;
status = res;
wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
" - status %u", MAC2STR(sm->addr), status);
@@ -1433,174 +3794,386 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
}
-static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+struct ft_get_sta_ctx {
+ const u8 *nonce;
+ const u8 *s1kh_id;
+ struct wpa_state_machine *sm;
+};
+
+
+static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
{
- struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+ struct ft_get_sta_ctx *info = ctx;
- if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
- return 0;
- if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
- FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
- return 0;
- if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+ if ((info->s1kh_id &&
+ os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+ os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
return 0;
- wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
- MACSTR " - process from timeout", MAC2STR(sm->addr));
- eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+ info->sm = sm;
+
return 1;
}
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
- const u8 *data, size_t data_len)
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
{
- struct ft_r0kh_r1kh_resp_frame f;
- const u8 *crypt;
- u8 *plain;
- struct ft_remote_r0kh *r0kh;
- int pairwise, res;
+ const char *msgtype = "pull response";
+ int nak, ret = -1;
+ struct ft_get_sta_ctx ctx;
+ u8 s1kh_id[ETH_ALEN];
+ const u8 *f_nonce;
+ size_t f_nonce_len;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
- if (data_len < sizeof(f))
- return -1;
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
- r0kh = wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r0kh = r0kh->next;
- }
- if (r0kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
- "PMK-R0 pull response source address " MACSTR,
- MAC2STR(src_addr));
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.nonce = f_nonce;
+ if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+ /* nonce not found */
+ wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
return -1;
}
- crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
- os_memset(&f, 0, sizeof(f));
- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
- /* aes_unwrap() does not support inplace decryption, so use a temporary
- * buffer for the data. */
- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
- (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
- crypt, plain) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
- "response from " MACSTR, MAC2STR(src_addr));
+ ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
+ enc, enc_len, auth, auth_len, msgtype, s1kh_id,
+ no_defer ? NULL : &wpa_ft_rrb_rx_resp);
+ if (ret == -2) {
+ ret = 0;
+ nak = 1;
+ } else {
+ nak = 0;
+ }
+ if (ret < 0)
return -1;
+
+ ctx.s1kh_id = s1kh_id;
+ if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Response to a pending pull request for " MACSTR,
+ MAC2STR(ctx.sm->addr));
+ eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
+ if (nak)
+ ctx.sm->ft_pending_pull_left_retries = 0;
+ ft_finish_pull(ctx.sm);
}
- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
- "matching R1KH-ID");
- return -1;
- }
-
- pairwise = le_to_host16(f.pairwise);
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
- f.nonce, sizeof(f.nonce));
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
- MACSTR " pairwise=0x%x",
- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
- f.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
- f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
- res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
- pairwise);
- wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
- wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
- os_memset(f.pmk_r1, 0, PMK_LEN);
-
- return res ? 0 : -1;
+out:
+ return ret;
}
static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
- const u8 *data, size_t data_len)
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len, int no_defer)
{
- struct ft_r0kh_r1kh_push_frame f;
- const u8 *crypt;
- u8 *plain;
- struct ft_remote_r0kh *r0kh;
- struct os_time now;
- os_time_t tsend;
- int pairwise;
+ const char *msgtype = "push";
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
- if (data_len < sizeof(f))
+ if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
+ enc, enc_len, auth, auth_len, msgtype, NULL,
+ no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
return -1;
- r0kh = wpa_auth->conf.r0kh_list;
- while (r0kh) {
- if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
- break;
- r0kh = r0kh->next;
- }
- if (r0kh == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
- "PMK-R0 push source address " MACSTR,
- MAC2STR(src_addr));
- return -1;
- }
-
- crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
- os_memset(&f, 0, sizeof(f));
- plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
- timestamp);
- /* aes_unwrap() does not support inplace decryption, so use a temporary
- * buffer for the data. */
- if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
- crypt, plain) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
- MACSTR, MAC2STR(src_addr));
- return -1;
- }
-
- os_get_time(&now);
- tsend = WPA_GET_LE32(f.timestamp);
- if ((now.sec > tsend && now.sec - tsend > 60) ||
- (now.sec < tsend && tsend - now.sec > 60)) {
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
- "timestamp: sender time %d own time %d\n",
- (int) tsend, (int) now.sec);
- return -1;
- }
-
- if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
- "R1KH-ID (received " MACSTR " own " MACSTR ")",
- MAC2STR(f.r1kh_id),
- MAC2STR(wpa_auth->conf.r1_key_holder));
- return -1;
- }
-
- pairwise = le_to_host16(f.pairwise);
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
- MACSTR " pairwise=0x%x",
- MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1",
- f.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name",
- f.pmk_r1_name, WPA_PMK_NAME_LEN);
-
- wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
- pairwise);
- os_memset(f.pmk_r1, 0, PMK_LEN);
-
return 0;
}
+static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr, int type,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ struct ft_remote_seq **rkh_seq,
+ u8 **key, size_t *key_len,
+ struct ft_remote_r0kh **r0kh_out,
+ struct ft_remote_r1kh **r1kh_out,
+ struct ft_remote_r0kh **r0kh_wildcard_out,
+ struct ft_remote_r1kh **r1kh_wildcard_out)
+{
+ struct ft_remote_r0kh *r0kh = NULL;
+ struct ft_remote_r1kh *r1kh = NULL;
+ const u8 *f_r0kh_id, *f_r1kh_id;
+ size_t f_r0kh_id_len, f_r1kh_id_len;
+ int to_r0kh, to_r1kh;
+ u8 *plain = NULL;
+ size_t plain_len = 0;
+ struct ft_remote_r0kh *r0kh_wildcard;
+ struct ft_remote_r1kh *r1kh_wildcard;
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+ to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
+ to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
+
+ if (to_r0kh && to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
+ goto out;
+ }
+
+ if (!to_r0kh && !to_r1kh) {
+ wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
+ goto out;
+ }
+
+ if (!to_r0kh) {
+ wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
+ &r0kh, &r0kh_wildcard);
+ if (!r0kh_wildcard &&
+ (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ f_r0kh_id, f_r0kh_id_len);
+ goto out;
+ }
+ if (r0kh) {
+ *key = r0kh->key;
+ *key_len = sizeof(r0kh->key);
+ } else {
+ *key = r0kh_wildcard->key;
+ *key_len = sizeof(r0kh_wildcard->key);
+ }
+ }
+
+ if (!to_r1kh) {
+ wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
+ &r1kh_wildcard);
+ if (!r1kh_wildcard &&
+ (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
+ f_r1kh_id, FT_R1KH_ID_LEN);
+ goto out;
+ }
+ if (r1kh) {
+ *key = r1kh->key;
+ *key_len = sizeof(r1kh->key);
+ } else {
+ *key = r1kh_wildcard->key;
+ *key_len = sizeof(r1kh_wildcard->key);
+ }
+ }
+
+ if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
+ src_addr, type, &plain, &plain_len) < 0)
+ goto out;
+
+ os_free(plain);
+
+ if (!to_r0kh) {
+ if (!r0kh)
+ r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
+ src_addr, f_r0kh_id,
+ f_r0kh_id_len,
+ ftRRBseqTimeout);
+ if (!r0kh)
+ goto out;
+
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
+ *rkh_seq = r0kh->seq;
+ if (r0kh_out)
+ *r0kh_out = r0kh;
+ if (r0kh_wildcard_out)
+ *r0kh_wildcard_out = r0kh_wildcard;
+ }
+
+ if (!to_r1kh) {
+ if (!r1kh)
+ r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
+ src_addr, f_r1kh_id,
+ ftRRBseqTimeout);
+ if (!r1kh)
+ goto out;
+
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
+ *rkh_seq = r1kh->seq;
+ if (r1kh_out)
+ *r1kh_out = r1kh;
+ if (r1kh_wildcard_out)
+ *r1kh_wildcard_out = r1kh_wildcard;
+ }
+
+ return 0;
+out:
+ return -1;
+}
+
+
+static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ int ret = -1;
+ struct ft_rrb_seq f_seq;
+ const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
+ size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ u8 *packet = NULL, *key = NULL;
+ size_t packet_len = 0, key_len = 0;
+ struct tlv_list seq_resp_auth[5];
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
+
+ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ enc, enc_len, auth, auth_len, &rkh_seq, &key,
+ &key_len, NULL, NULL, NULL, NULL) < 0)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
+
+ RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
+ RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
+
+ if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ goto out;
+ }
+
+ seq_resp_auth[0].type = FT_RRB_NONCE;
+ seq_resp_auth[0].len = f_nonce_len;
+ seq_resp_auth[0].data = f_nonce;
+ seq_resp_auth[1].type = FT_RRB_SEQ;
+ seq_resp_auth[1].len = sizeof(f_seq);
+ seq_resp_auth[1].data = (u8 *) &f_seq;
+ seq_resp_auth[2].type = FT_RRB_R0KH_ID;
+ seq_resp_auth[2].len = f_r0kh_id_len;
+ seq_resp_auth[2].data = f_r0kh_id;
+ seq_resp_auth[3].type = FT_RRB_R1KH_ID;
+ seq_resp_auth[3].len = FT_R1KH_ID_LEN;
+ seq_resp_auth[3].data = f_r1kh_id;
+ seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
+ seq_resp_auth[4].len = 0;
+ seq_resp_auth[4].data = NULL;
+
+ if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
+ wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ &packet, &packet_len) < 0)
+ goto out;
+
+ wpa_ft_rrb_oui_send(wpa_auth, src_addr,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
+ packet_len);
+
+out:
+ os_free(packet);
+
+ return ret;
+}
+
+
+static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
+ const u8 *src_addr,
+ const u8 *enc, size_t enc_len,
+ const u8 *auth, size_t auth_len,
+ int no_defer)
+{
+ u8 *key = NULL;
+ size_t key_len = 0;
+ struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
+ struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
+ const u8 *f_nonce, *f_seq;
+ size_t f_nonce_len, f_seq_len;
+ struct ft_remote_seq *rkh_seq = NULL;
+ struct ft_remote_item *item;
+ struct os_reltime now, now_remote;
+ int seq_ret, found;
+ const struct ft_rrb_seq *msg_both;
+ u32 msg_dom, msg_seq;
+
+ wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
+
+ if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ enc, enc_len, auth, auth_len, &rkh_seq, &key,
+ &key_len, &r0kh, &r1kh, &r0kh_wildcard,
+ &r1kh_wildcard) < 0)
+ goto out;
+
+ RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
+ f_nonce_len);
+
+ found = 0;
+ dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
+ list) {
+ if (os_memcmp_const(f_nonce, item->nonce,
+ FT_RRB_NONCE_LEN) != 0 ||
+ os_get_reltime(&now) < 0 ||
+ os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
+ continue;
+
+ found = 1;
+ break;
+ }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
+ goto out;
+ }
+
+ if (r0kh) {
+ wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (r0kh_wildcard)
+ os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
+ }
+
+ if (r1kh) {
+ wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
+ wpa_auth->conf.rkh_pos_timeout);
+ if (r1kh_wildcard)
+ os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
+ }
+
+ seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
+ auth_len, "seq response", 1);
+ if (seq_ret == FT_RRB_SEQ_OK) {
+ wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
+ wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
+ auth_len, "seq response");
+ } else {
+ wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
+
+ RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
+ sizeof(*msg_both));
+ msg_both = (const struct ft_rrb_seq *) f_seq;
+
+ msg_dom = le_to_host32(msg_both->dom);
+ msg_seq = le_to_host32(msg_both->seq);
+ now_remote.sec = le_to_host32(msg_both->ts);
+ now_remote.usec = 0;
+
+ rkh_seq->rx.num_last = 2;
+ rkh_seq->rx.dom = msg_dom;
+ rkh_seq->rx.offsetidx = 0;
+ /* Accept some older, possibly cached packets as well */
+ rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
+ dl_list_len(&rkh_seq->rx.queue);
+ rkh_seq->rx.last[1] = msg_seq;
+
+ /* local time - offset = remote time
+ * <=> local time - remote time = offset */
+ os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
+ }
+
+ wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
+
+ return 0;
+out:
+ return -1;
+}
+
+
int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
const u8 *data, size_t data_len)
{
@@ -1642,13 +4215,6 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
return -1;
}
- if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
- return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
- if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
- return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
- if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
- return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
-
wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
if (alen < 1 + 1 + 2 * ETH_ALEN) {
@@ -1726,65 +4292,137 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
}
-static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
- struct wpa_ft_pmk_r0_sa *pmk_r0,
- struct ft_remote_r1kh *r1kh,
- const u8 *s1kh_id, int pairwise)
+void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix, const u8 *data,
+ size_t data_len)
{
- struct ft_r0kh_r1kh_push_frame frame, f;
- struct os_time now;
- const u8 *plain;
- u8 *crypt;
+ const u8 *auth, *enc;
+ size_t alen, elen;
+ int no_defer = 0;
- os_memset(&frame, 0, sizeof(frame));
- frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
- frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
- frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
- os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+ 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);
- /* aes_wrap() does not support inplace encryption, so use a temporary
- * buffer for the data. */
- os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
- os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
- os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
- s1kh_id, f.pmk_r1, f.pmk_r1_name);
- wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
- wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
- WPA_PMK_NAME_LEN);
- os_get_time(&now);
- WPA_PUT_LE32(f.timestamp, now.sec);
- f.pairwise = host_to_le16(pairwise);
- os_memset(f.pad, 0, sizeof(f.pad));
- plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
- timestamp);
- crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
- timestamp);
- if (aes_wrap(r1kh->key, sizeof(r1kh->key),
- (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
- plain, crypt) < 0)
+ if (is_multicast_ether_addr(src_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB-OUI received frame from multicast address "
+ MACSTR, MAC2STR(src_addr));
return;
+ }
- wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+ if (is_multicast_ether_addr(dst_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RRB-OUI received frame from remote AP " MACSTR
+ " to multicast address " MACSTR,
+ MAC2STR(src_addr), MAC2STR(dst_addr));
+ no_defer = 1;
+ }
+
+ if (data_len < sizeof(u16)) {
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+ return;
+ }
+
+ alen = WPA_GET_LE16(data);
+ if (data_len < sizeof(u16) + alen) {
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
+ return;
+ }
+
+ auth = data + sizeof(u16);
+ enc = data + sizeof(u16) + alen;
+ elen = data_len - sizeof(u16) - alen;
+
+ switch (oui_suffix) {
+ case FT_PACKET_R0KH_R1KH_PULL:
+ wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_RESP:
+ wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_PUSH:
+ wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
+ no_defer);
+ break;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
+ alen, no_defer);
+ break;
+ }
+}
+
+
+static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+ struct wpa_ft_pmk_r0_sa *pmk_r0,
+ struct ft_remote_r1kh *r1kh,
+ const u8 *s1kh_id)
+{
+ u8 *packet;
+ size_t packet_len;
+ struct ft_rrb_seq f_seq;
+ struct tlv_list push[] = {
+ { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
+ .data = s1kh_id },
+ { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
+ .data = pmk_r0->pmk_r0_name },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+ struct tlv_list push_auth[] = {
+ { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
+ .data = (u8 *) &f_seq },
+ { .type = FT_RRB_R0KH_ID,
+ .len = wpa_auth->conf.r0_key_holder_len,
+ .data = wpa_auth->conf.r0_key_holder },
+ { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
+ .data = r1kh->id },
+ { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
+ };
+
+ if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
+ return -1;
+ }
+
+ if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
+ r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
+ FT_PACKET_R0KH_R1KH_PUSH,
+ &packet, &packet_len) < 0)
+ return -1;
+
+ wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
+ packet, packet_len);
+
+ os_free(packet);
+ return 0;
}
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
{
- struct wpa_ft_pmk_r0_sa *r0;
+ struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
+ struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
struct ft_remote_r1kh *r1kh;
if (!wpa_auth->conf.pmk_r1_push)
return;
+ if (!wpa_auth->conf.r1kh_list)
+ return;
- r0 = wpa_auth->ft_pmk_cache->pmk_r0;
- while (r0) {
- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
+ dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
+ if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ r0found = r0;
break;
- r0 = r0->next;
+ }
}
+ r0 = r0found;
if (r0 == NULL || r0->pmk_r1_pushed)
return;
r0->pmk_r1_pushed = 1;
@@ -1792,11 +4430,14 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
"for STA " MACSTR, MAC2STR(addr));
- r1kh = wpa_auth->conf.r1kh_list;
- while (r1kh) {
- wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
- r1kh = r1kh->next;
+ for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
+ if (is_zero_ether_addr(r1kh->addr) ||
+ is_zero_ether_addr(r1kh->id))
+ continue;
+ if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
+ continue;
+ wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
}
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 21424147e443..812740301c8b 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -9,6 +9,8 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "common/wpa_ctrl.h"
@@ -17,6 +19,7 @@
#include "eapol_auth/eapol_auth_sm_i.h"
#include "eap_server/eap.h"
#include "l2_packet/l2_packet.h"
+#include "eth_p_oui.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "preauth_auth.h"
@@ -24,6 +27,7 @@
#include "tkip_countermeasures.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "pmksa_cache_auth.h"
#include "wpa_auth.h"
#include "wpa_auth_glue.h"
@@ -41,10 +45,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+ wconf->wpa_group_update_count = conf->wpa_group_update_count;
+ wconf->wpa_disable_eapol_key_retries =
+ conf->wpa_disable_eapol_key_retries;
+ wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
wconf->rsn_pairwise = conf->rsn_pairwise;
wconf->rsn_preauth = conf->rsn_preauth;
wconf->eapol_version = conf->eapol_version;
- wconf->peerkey = conf->peerkey;
wconf->wmm_enabled = conf->wmm_enabled;
wconf->wmm_uapsd = conf->wmm_uapsd;
wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
@@ -52,8 +59,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
+ wconf->sae_require_mfp = conf->sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
wconf->ssid_len = conf->ssid.ssid_len;
if (wconf->ssid_len > SSID_MAX_LEN)
wconf->ssid_len = SSID_MAX_LEN;
@@ -68,12 +76,18 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
}
os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
wconf->r0_key_lifetime = conf->r0_key_lifetime;
+ wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime;
wconf->reassociation_deadline = conf->reassociation_deadline;
- wconf->r0kh_list = conf->r0kh_list;
- wconf->r1kh_list = conf->r1kh_list;
+ wconf->rkh_pos_timeout = conf->rkh_pos_timeout;
+ wconf->rkh_neg_timeout = conf->rkh_neg_timeout;
+ wconf->rkh_pull_timeout = conf->rkh_pull_timeout;
+ wconf->rkh_pull_retries = conf->rkh_pull_retries;
+ wconf->r0kh_list = &conf->r0kh_list;
+ wconf->r1kh_list = &conf->r1kh_list;
wconf->pmk_r1_push = conf->pmk_r1_push;
wconf->ft_over_ds = conf->ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+ wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_HS20
wconf->disable_gtk = conf->disable_dgaf;
if (conf->osen) {
@@ -107,6 +121,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
#endif /* CONFIG_P2P */
+#ifdef CONFIG_FILS
+ wconf->fils_cache_id_set = conf->fils_cache_id_set;
+ os_memcpy(wconf->fils_cache_id, conf->fils_cache_id,
+ FILS_CACHE_ID_LEN);
+#endif /* CONFIG_FILS */
}
@@ -223,20 +242,47 @@ 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)
+ const u8 *prev_psk, size_t *psk_len)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
const u8 *psk;
+ if (psk_len)
+ *psk_len = PMK_LEN;
+
#ifdef CONFIG_SAE
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
return NULL;
return sta->sae->pmk;
}
+ if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG,
+ "No PSK for STA trying to use SAE with PMKSA caching");
+ return NULL;
+ }
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_pmk) {
+ if (psk_len)
+ *psk_len = sta->owe_pmk_len;
+ return sta->owe_pmk;
+ }
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) {
+ struct rsn_pmksa_cache_entry *sa;
+
+ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (sa && sa->akmp == WPA_KEY_MGMT_OWE) {
+ if (psk_len)
+ *psk_len = sa->pmk_len;
+ return sa->pmk;
+ }
+ }
+#endif /* CONFIG_OWE */
+
psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
/*
* This is about to iterate over all psks, prev_psk gives the last
@@ -307,6 +353,37 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
return -1;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (addr && !is_broadcast_ether_addr(addr)) {
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta) {
+ sta->last_tk_alg = alg;
+ sta->last_tk_key_idx = idx;
+ if (key)
+ os_memcpy(sta->last_tk, key, key_len);
+ sta->last_tk_len = key_len;
+ }
+#ifdef CONFIG_IEEE80211W
+ } else if (alg == WPA_ALG_IGTK ||
+ alg == WPA_ALG_BIP_GMAC_128 ||
+ alg == WPA_ALG_BIP_GMAC_256 ||
+ alg == WPA_ALG_BIP_CMAC_256) {
+ hapd->last_igtk_alg = alg;
+ hapd->last_igtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_igtk, key, key_len);
+ hapd->last_igtk_len = key_len;
+#endif /* CONFIG_IEEE80211W */
+ } else {
+ hapd->last_gtk_alg = alg;
+ hapd->last_gtk_key_idx = idx;
+ if (key)
+ os_memcpy(hapd->last_gtk, key, key_len);
+ hapd->last_gtk_len = key_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
key, key_len);
}
@@ -401,7 +478,32 @@ static int hostapd_wpa_auth_for_each_auth(
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+
+struct wpa_ft_rrb_rx_later_data {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ size_t data_len;
+ /* followed by data_len octets of data */
+};
+
+static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct wpa_ft_rrb_rx_later_data *data, *n;
+
+ dl_list_for_each_safe(data, n, &hapd->l2_queue,
+ struct wpa_ft_rrb_rx_later_data, list) {
+ if (hapd->wpa_auth) {
+ wpa_ft_rrb_rx(hapd->wpa_auth, data->addr,
+ (const u8 *) (data + 1),
+ data->data_len);
+ }
+ dl_list_del(&data->list);
+ os_free(data);
+ }
+}
+
struct wpa_auth_ft_iface_iter_data {
struct hostapd_data *src_hapd;
@@ -414,33 +516,54 @@ struct wpa_auth_ft_iface_iter_data {
static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
{
struct wpa_auth_ft_iface_iter_data *idata = ctx;
+ struct wpa_ft_rrb_rx_later_data *data;
struct hostapd_data *hapd;
size_t j;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
- if (hapd == idata->src_hapd)
+ if (hapd == idata->src_hapd ||
+ !hapd->wpa_auth ||
+ os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
continue;
- if (!hapd->wpa_auth)
- continue;
- if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
- wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
- "locally managed BSS " MACSTR "@%s -> "
- MACSTR "@%s",
- MAC2STR(idata->src_hapd->own_addr),
- idata->src_hapd->conf->iface,
- MAC2STR(hapd->own_addr), hapd->conf->iface);
- wpa_ft_rrb_rx(hapd->wpa_auth,
- idata->src_hapd->own_addr,
- idata->data, idata->data_len);
+
+ wpa_printf(MSG_DEBUG,
+ "FT: Send RRB data directly to locally managed BSS "
+ MACSTR "@%s -> " MACSTR "@%s",
+ MAC2STR(idata->src_hapd->own_addr),
+ idata->src_hapd->conf->iface,
+ MAC2STR(hapd->own_addr), hapd->conf->iface);
+
+ /* Defer wpa_ft_rrb_rx() until next eloop step as this is
+ * when it would be triggered when reading from a socket.
+ * This avoids
+ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+ * that is calling hapd0:recv handler from within
+ * hapd0:send directly.
+ */
+ data = os_zalloc(sizeof(*data) + idata->data_len);
+ if (!data)
return 1;
- }
+
+ os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN);
+ os_memcpy(data + 1, idata->data, idata->data_len);
+ data->data_len = idata->data_len;
+
+ dl_list_add(&hapd->l2_queue, &data->list);
+
+ if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later,
+ hapd, NULL))
+ eloop_register_timeout(0, 0,
+ hostapd_wpa_ft_rrb_rx_later,
+ hapd, NULL);
+
+ return 1;
}
return 0;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
@@ -465,7 +588,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
}
#endif /* CONFIG_TESTING_OPTIONS */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (proto == ETH_P_RRB && hapd->iface->interfaces &&
hapd->iface->interfaces->for_each_interface) {
int res;
@@ -480,7 +603,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
if (res == 1)
return data_len;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (hapd->driver && hapd->driver->send_ether)
return hapd->driver->send_ether(hapd->drv_priv, dst,
@@ -503,7 +626,157 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_ETH_P_OUI
+static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd,
+ u8 oui_suffix)
+{
+ switch (oui_suffix) {
+#ifdef CONFIG_IEEE80211R_AP
+ case FT_PACKET_R0KH_R1KH_PULL:
+ return hapd->oui_pull;
+ case FT_PACKET_R0KH_R1KH_RESP:
+ return hapd->oui_resp;
+ case FT_PACKET_R0KH_R1KH_PUSH:
+ return hapd->oui_push;
+ case FT_PACKET_R0KH_R1KH_SEQ_REQ:
+ return hapd->oui_sreq;
+ case FT_PACKET_R0KH_R1KH_SEQ_RESP:
+ return hapd->oui_sresp;
+#endif /* CONFIG_IEEE80211R_AP */
+ default:
+ return NULL;
+ }
+}
+#endif /* CONFIG_ETH_P_OUI */
+
+
+#ifdef CONFIG_IEEE80211R_AP
+
+struct oui_deliver_later_data {
+ struct dl_list list;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+ size_t data_len;
+ u8 oui_suffix;
+ /* followed by data_len octets of data */
+};
+
+static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct oui_deliver_later_data *data, *n;
+ struct eth_p_oui_ctx *oui_ctx;
+
+ dl_list_for_each_safe(data, n, &hapd->l2_oui_queue,
+ struct oui_deliver_later_data, list) {
+ oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix);
+ if (hapd->wpa_auth && oui_ctx) {
+ eth_p_oui_deliver(oui_ctx, data->src_addr,
+ data->dst_addr,
+ (const u8 *) (data + 1),
+ data->data_len);
+ }
+ dl_list_del(&data->list);
+ os_free(data);
+ }
+}
+
+
+struct wpa_auth_oui_iface_iter_data {
+ struct hostapd_data *src_hapd;
+ const u8 *dst_addr;
+ const u8 *data;
+ size_t data_len;
+ u8 oui_suffix;
+};
+
+static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx)
+{
+ struct wpa_auth_oui_iface_iter_data *idata = ctx;
+ struct oui_deliver_later_data *data;
+ struct hostapd_data *hapd;
+ size_t j;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (hapd == idata->src_hapd)
+ continue;
+ if (!is_multicast_ether_addr(idata->dst_addr) &&
+ os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+ continue;
+
+ /* defer eth_p_oui_deliver until next eloop step as this is
+ * when it would be triggerd from reading from sock
+ * This avoids
+ * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv,
+ * that is calling hapd0:recv handler from within
+ * hapd0:send directly.
+ */
+ data = os_zalloc(sizeof(*data) + idata->data_len);
+ if (!data)
+ return 1;
+
+ os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN);
+ os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN);
+ os_memcpy(data + 1, idata->data, idata->data_len);
+ data->data_len = idata->data_len;
+ data->oui_suffix = idata->oui_suffix;
+
+ dl_list_add(&hapd->l2_oui_queue, &data->list);
+
+ if (!eloop_is_timeout_registered(hostapd_oui_deliver_later,
+ hapd, NULL))
+ eloop_register_timeout(0, 0,
+ hostapd_oui_deliver_later,
+ hapd, NULL);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
+static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
+ const u8 *data, size_t data_len)
+{
+#ifdef CONFIG_ETH_P_OUI
+ struct hostapd_data *hapd = ctx;
+ struct eth_p_oui_ctx *oui_ctx;
+
+#ifdef CONFIG_IEEE80211R_AP
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface) {
+ struct wpa_auth_oui_iface_iter_data idata;
+ int res;
+
+ idata.src_hapd = hapd;
+ idata.dst_addr = dst;
+ idata.data = data;
+ idata.data_len = data_len;
+ idata.oui_suffix = oui_suffix;
+ res = hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_wpa_auth_oui_iter,
+ &idata);
+ if (res == 1)
+ return data_len;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+
+ oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix);
+ if (!oui_ctx)
+ return -1;
+
+ return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len);
+#else /* CONFIG_ETH_P_OUI */
+ return -1;
+#endif /* CONFIG_ETH_P_OUI */
+}
+
+
+#ifdef CONFIG_IEEE80211R_AP
static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
const u8 *data, size_t data_len)
@@ -563,6 +836,244 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
}
+static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ? "+" : "");
+ return -1;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+ /* Configure wpa_group for GTK but ignore error due to driver not
+ * knowing this STA. */
+ ap_sta_bind_vlan(hapd, sta);
+
+ if (sta->vlan_id)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+ return 0;
+}
+
+
+static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr,
+ struct vlan_description *vlan)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ if (sta->vlan_desc)
+ *vlan = *sta->vlan_desc;
+ else
+ os_memset(vlan, 0, sizeof(*vlan));
+
+ return 0;
+}
+
+
+static int
+hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr,
+ const u8 *identity, size_t identity_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->identity);
+ sta->identity = NULL;
+
+ if (sta->eapol_sm) {
+ os_free(sta->eapol_sm->identity);
+ sta->eapol_sm->identity = NULL;
+ sta->eapol_sm->identity_len = 0;
+ }
+
+ if (!identity_len)
+ return 0;
+
+ /* sta->identity is NULL terminated */
+ sta->identity = os_zalloc(identity_len + 1);
+ if (!sta->identity)
+ return -1;
+ os_memcpy(sta->identity, identity, identity_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->identity = os_zalloc(identity_len);
+ if (!sta->eapol_sm->identity)
+ return -1;
+ os_memcpy(sta->eapol_sm->identity, identity, identity_len);
+ sta->eapol_sm->identity_len = identity_len;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ size_t len;
+ char *identity;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ *buf = ieee802_1x_get_identity(sta->eapol_sm, &len);
+ if (*buf && len)
+ return len;
+
+ if (!sta->identity) {
+ *buf = NULL;
+ return 0;
+ }
+
+ identity = sta->identity;
+ len = os_strlen(identity);
+ *buf = (u8 *) identity;
+
+ return len;
+}
+
+
+static int
+hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr,
+ const u8 *radius_cui, size_t radius_cui_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return -1;
+
+ os_free(sta->radius_cui);
+ sta->radius_cui = NULL;
+
+ if (sta->eapol_sm) {
+ wpabuf_free(sta->eapol_sm->radius_cui);
+ sta->eapol_sm->radius_cui = NULL;
+ }
+
+ if (!radius_cui)
+ return 0;
+
+ /* sta->radius_cui is NULL terminated */
+ sta->radius_cui = os_zalloc(radius_cui_len + 1);
+ if (!sta->radius_cui)
+ return -1;
+ os_memcpy(sta->radius_cui, radius_cui, radius_cui_len);
+
+ if (sta->eapol_sm) {
+ sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+ radius_cui_len);
+ if (!sta->eapol_sm->radius_cui)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static size_t
+hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct wpabuf *b;
+ size_t len;
+ char *radius_cui;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return 0;
+
+ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (b) {
+ len = wpabuf_len(b);
+ *buf = wpabuf_head(b);
+ return len;
+ }
+
+ if (!sta->radius_cui) {
+ *buf = NULL;
+ return 0;
+ }
+
+ radius_cui = sta->radius_cui;
+ len = os_strlen(radius_cui);
+ *buf = (u8 *) radius_cui;
+
+ return len;
+}
+
+
+static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr,
+ int session_timeout)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta)
+ return;
+
+ if (session_timeout) {
+ os_get_reltime(&sta->session_timeout);
+ sta->session_timeout.sec += session_timeout;
+ sta->session_timeout_set = 1;
+ ap_sta_session_timeout(hapd, sta, session_timeout);
+ } else {
+ sta->session_timeout_set = 0;
+ ap_sta_no_session_timeout(hapd, sta);
+ }
+}
+
+
+static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct os_reltime now, remaining;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (!sta || !sta->session_timeout_set)
+ return 0;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&sta->session_timeout, &now)) {
+ /* already expired, return >0 as timeout was set */
+ return 1;
+ }
+
+ os_reltime_sub(&sta->session_timeout, &now, &remaining);
+
+ return (remaining.sec > 0) ? remaining.sec : 1;
+}
+
+
static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
@@ -581,6 +1092,22 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
}
+static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr,
+ const u8 *dst_addr, u8 oui_suffix,
+ const u8 *buf, size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
+ if (!is_multicast_ether_addr(dst_addr) &&
+ os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+ return;
+ wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
+ len);
+}
+
+
static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
u8 *tspec_ie, size_t tspec_ielen)
{
@@ -588,13 +1115,94 @@ static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
}
-#endif /* CONFIG_IEEE80211R */
+
+
+static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd,
+ const char *ft_iface)
+{
+ hapd->oui_pull = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_PULL,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_pull)
+ return -1;
+
+ hapd->oui_resp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_resp)
+ return -1;
+
+ hapd->oui_push = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_PUSH,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_push)
+ return -1;
+
+ hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_REQ,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sreq)
+ return -1;
+
+ hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface,
+ FT_PACKET_R0KH_R1KH_SEQ_RESP,
+ hostapd_rrb_oui_receive, hapd);
+ if (!hapd->oui_sresp)
+ return -1;
+
+ return 0;
+}
+
+
+static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd)
+{
+ eth_p_oui_unregister(hapd->oui_pull);
+ hapd->oui_pull = NULL;
+ eth_p_oui_unregister(hapd->oui_resp);
+ hapd->oui_resp = NULL;
+ eth_p_oui_unregister(hapd->oui_push);
+ hapd->oui_push = NULL;
+ eth_p_oui_unregister(hapd->oui_sreq);
+ hapd->oui_sreq = NULL;
+ eth_p_oui_unregister(hapd->oui_sresp);
+ hapd->oui_sresp = NULL;
+}
+#endif /* CONFIG_IEEE80211R_AP */
int hostapd_setup_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config _conf;
- struct wpa_auth_callbacks cb;
+ static const struct wpa_auth_callbacks cb = {
+ .logger = hostapd_wpa_auth_logger,
+ .disconnect = hostapd_wpa_auth_disconnect,
+ .mic_failure_report = hostapd_wpa_auth_mic_failure_report,
+ .psk_failure_report = hostapd_wpa_auth_psk_failure_report,
+ .set_eapol = hostapd_wpa_auth_set_eapol,
+ .get_eapol = hostapd_wpa_auth_get_eapol,
+ .get_psk = hostapd_wpa_auth_get_psk,
+ .get_msk = hostapd_wpa_auth_get_msk,
+ .set_key = hostapd_wpa_auth_set_key,
+ .get_seqnum = hostapd_wpa_auth_get_seqnum,
+ .send_eapol = hostapd_wpa_auth_send_eapol,
+ .for_each_sta = hostapd_wpa_auth_for_each_sta,
+ .for_each_auth = hostapd_wpa_auth_for_each_auth,
+ .send_ether = hostapd_wpa_auth_send_ether,
+ .send_oui = hostapd_wpa_auth_send_oui,
+#ifdef CONFIG_IEEE80211R_AP
+ .send_ft_action = hostapd_wpa_auth_send_ft_action,
+ .add_sta = hostapd_wpa_auth_add_sta,
+ .add_tspec = hostapd_wpa_auth_add_tspec,
+ .set_vlan = hostapd_wpa_auth_set_vlan,
+ .get_vlan = hostapd_wpa_auth_get_vlan,
+ .set_identity = hostapd_wpa_auth_set_identity,
+ .get_identity = hostapd_wpa_auth_get_identity,
+ .set_radius_cui = hostapd_wpa_auth_set_radius_cui,
+ .get_radius_cui = hostapd_wpa_auth_get_radius_cui,
+ .set_session_timeout = hostapd_wpa_auth_set_session_timeout,
+ .get_session_timeout = hostapd_wpa_auth_get_session_timeout,
+#endif /* CONFIG_IEEE80211R_AP */
+ };
const u8 *wpa_ie;
size_t wpa_ie_len;
@@ -603,28 +1211,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
_conf.tx_status = 1;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
_conf.ap_mlme = 1;
- os_memset(&cb, 0, sizeof(cb));
- cb.ctx = hapd;
- cb.logger = hostapd_wpa_auth_logger;
- cb.disconnect = hostapd_wpa_auth_disconnect;
- cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
- cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
- cb.set_eapol = hostapd_wpa_auth_set_eapol;
- cb.get_eapol = hostapd_wpa_auth_get_eapol;
- cb.get_psk = hostapd_wpa_auth_get_psk;
- cb.get_msk = hostapd_wpa_auth_get_msk;
- cb.set_key = hostapd_wpa_auth_set_key;
- cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
- cb.send_eapol = hostapd_wpa_auth_send_eapol;
- cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
- cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
- cb.send_ether = hostapd_wpa_auth_send_ether;
-#ifdef CONFIG_IEEE80211R
- cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
- cb.add_sta = hostapd_wpa_auth_add_sta;
- cb.add_tspec = hostapd_wpa_auth_add_tspec;
-#endif /* CONFIG_IEEE80211R */
- hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
+ hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed.");
return -1;
@@ -649,12 +1236,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
return -1;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (!hostapd_drv_none(hapd) &&
wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
- hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
- hapd->conf->bridge :
- hapd->conf->iface, NULL, ETH_P_RRB,
+ const char *ft_iface;
+
+ ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge :
+ hapd->conf->iface;
+ hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB,
hostapd_rrb_receive, hapd, 1);
if (hapd->l2 == NULL &&
(hapd->driver == NULL ||
@@ -663,8 +1252,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
"interface");
return -1;
}
+
+ if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to open ETH_P_OUI interface");
+ return -1;
+ }
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
return 0;
@@ -702,8 +1297,13 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd)
}
ieee802_1x_deinit(hapd);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
+ eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX);
+ hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */
+ eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX);
+ hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */
l2_packet_deinit(hapd->l2);
hapd->l2 = NULL;
-#endif /* CONFIG_IEEE80211R */
+ hostapd_wpa_unregister_ft_oui(hapd);
+#endif /* CONFIG_IEEE80211R_AP */
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 7fd8f05fa8ff..b1cea1b49b14 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -9,18 +9,13 @@
#ifndef WPA_AUTH_I_H
#define WPA_AUTH_I_H
+#include "utils/list.h"
+
/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
#define RSNA_MAX_EAPOL_RETRIES 4
struct wpa_group;
-struct wpa_stsl_negotiation {
- struct wpa_stsl_negotiation *next;
- u8 initiator[ETH_ALEN];
- u8 peer[ETH_ALEN];
-};
-
-
struct wpa_state_machine {
struct wpa_authenticator *wpa_auth;
struct wpa_group *group;
@@ -48,8 +43,9 @@ struct wpa_state_machine {
Boolean AuthenticationRequest;
Boolean ReAuthenticationRequest;
Boolean Disconnect;
- int TimeoutCtr;
- int GTimeoutCtr;
+ u16 disconnect_reason; /* specific reason code to use with Disconnect */
+ u32 TimeoutCtr;
+ u32 GTimeoutCtr;
Boolean TimeoutEvt;
Boolean EAPOLKeyReceived;
Boolean EAPOLKeyPairwise;
@@ -62,6 +58,7 @@ struct wpa_state_machine {
u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
u8 PMK[PMK_LEN_MAX];
unsigned int pmk_len;
+ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
@@ -89,11 +86,12 @@ struct wpa_state_machine {
unsigned int rx_eapol_key_secure:1;
unsigned int update_snonce:1;
unsigned int alt_snonce_valid:1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
unsigned int ft_completed:1;
unsigned int pmk_r1_name_valid:1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
+ unsigned int pmkid_set:1;
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
int req_replay_counter_used;
@@ -113,8 +111,9 @@ struct wpa_state_machine {
u32 dot11RSNAStatsTKIPLocalMICFailures;
u32 dot11RSNAStatsTKIPRemoteMICFailures;
-#ifdef CONFIG_IEEE80211R
- u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+#ifdef CONFIG_IEEE80211R_AP
+ 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_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
* Request */
@@ -129,16 +128,30 @@ struct wpa_state_machine {
const u8 *ies, size_t ies_len);
void *ft_pending_cb_ctx;
struct wpabuf *ft_pending_req_ies;
- u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+ u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN];
u8 ft_pending_auth_transaction;
u8 ft_pending_current_ap[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R */
+ int ft_pending_pull_left_retries;
+#endif /* CONFIG_IEEE80211R_AP */
int pending_1_of_4_timeout;
#ifdef CONFIG_P2P
u8 ip_addr[4];
#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_FILS
+ u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+ u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+ size_t fils_key_auth_len;
+ unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ void (*eapol_status_cb)(void *ctx1, void *ctx2);
+ void *eapol_status_cb_ctx1;
+ void *eapol_status_cb_ctx2;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -194,10 +207,9 @@ struct wpa_authenticator {
unsigned int dot11RSNATKIPCounterMeasuresInvoked;
unsigned int dot11RSNA4WayHandshakeFailures;
- struct wpa_stsl_negotiation *stsl_negotiations;
-
struct wpa_auth_config conf;
- struct wpa_auth_callbacks cb;
+ const struct wpa_auth_callbacks *cb;
+ void *cb_ctx;
u8 *wpa_ie;
size_t wpa_ie_len;
@@ -213,6 +225,38 @@ struct wpa_authenticator {
};
+#ifdef CONFIG_IEEE80211R_AP
+
+#define FT_REMOTE_SEQ_BACKLOG 16
+struct ft_remote_seq_rx {
+ u32 dom;
+ struct os_reltime time_offset; /* local time - offset = remote time */
+
+ /* accepted sequence numbers: (offset ... offset + 0x40000000]
+ * (except those in last)
+ * dropped sequence numbers: (offset - 0x40000000 ... offset]
+ * all others trigger SEQ_REQ message (except first message)
+ */
+ u32 last[FT_REMOTE_SEQ_BACKLOG];
+ unsigned int num_last;
+ u32 offsetidx;
+
+ struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */
+};
+
+struct ft_remote_seq_tx {
+ u32 dom; /* non zero if initialized */
+ u32 seq;
+};
+
+struct ft_remote_seq {
+ struct ft_remote_seq_rx rx;
+ struct ft_remote_seq_tx tx;
+};
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
const u8 *pmkid);
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
@@ -231,24 +275,10 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx);
-#ifdef CONFIG_PEERKEY
-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
- struct wpa_stsl_negotiation *neg);
-void wpa_smk_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
- const u8 *key_data, size_t key_data_len);
-void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len);
-void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key,
- const u8 *key_data, size_t key_data_len);
-#endif /* CONFIG_PEERKEY */
-
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
-int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
- size_t r0kh_id_len,
+int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
+ const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len);
@@ -257,6 +287,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
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);
-#endif /* CONFIG_IEEE80211R */
+int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0,
+ const u8 *pmk_r0_name);
+#endif /* CONFIG_IEEE80211R_AP */
#endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f79783b91929..cdcc5de39d45 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1,6 +1,6 @@
/*
* hostapd - WPA/RSN IE and KDE definitions
- * Copyright (c) 2004-2015, Jouni Malinen
+ * Copyright (c) 2004-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -164,18 +164,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
pos += RSN_SELECTOR_LEN;
num_suites++;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#ifdef CONFIG_SHA384
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SHA384 */
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
@@ -210,6 +217,51 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#ifdef CONFIG_FILS
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#ifdef CONFIG_IEEE80211R_AP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_HS20 */
#ifdef CONFIG_RSN_TESTING
if (rsn_testing) {
@@ -230,8 +282,6 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
capab = 0;
if (conf->rsn_preauth)
capab |= WPA_CAPABILITY_PREAUTH;
- if (conf->peerkey)
- capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
if (conf->wmm_enabled) {
/* 4 PTKSA replay counters when using WMM */
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
@@ -407,7 +457,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
return res;
pos += res;
}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
res = wpa_write_mdie(&wpa_auth->conf, pos,
buf + sizeof(buf) - pos);
@@ -415,7 +465,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
return res;
pos += res;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
res = wpa_write_wpa_ie(&wpa_auth->conf,
pos, buf + sizeof(buf) - pos);
@@ -474,7 +524,8 @@ 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,
const u8 *wpa_ie, size_t wpa_ie_len,
- const u8 *mdie, size_t mdie_len)
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len)
{
struct wpa_ie_data data;
int ciphers, key_mgmt, res, version;
@@ -509,12 +560,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
selector = RSN_AUTH_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
@@ -531,6 +598,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+#ifdef CONFIG_OWE
+ else if (data.key_mgmt & WPA_KEY_MGMT_OWE)
+ selector = RSN_AUTH_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (data.key_mgmt & WPA_KEY_MGMT_DPP)
+ selector = RSN_AUTH_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (data.key_mgmt & WPA_KEY_MGMT_OSEN)
+ selector = RSN_AUTH_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
@@ -591,12 +670,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+ else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+ else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+#ifdef CONFIG_SHA384
+ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
@@ -611,6 +706,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_SAE */
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+#ifdef CONFIG_OWE
+ else if (key_mgmt & WPA_KEY_MGMT_OWE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+ else if (key_mgmt & WPA_KEY_MGMT_DPP)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+ else if (key_mgmt & WPA_KEY_MGMT_OSEN)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
else
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
@@ -634,12 +741,6 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
}
- if (ciphers & WPA_CIPHER_TKIP) {
- wpa_printf(MSG_DEBUG, "Management frame protection "
- "cannot use TKIP");
- return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
- }
-
if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
{
wpa_printf(MSG_DEBUG, "Unsupported management group "
@@ -648,14 +749,31 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
}
+#ifdef CONFIG_SAE
+ if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL &&
+ wpa_auth->conf.sae_require_mfp &&
+ wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with SAE, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+#endif /* CONFIG_SAE */
+
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
!(data.capabilities & WPA_CAPABILITY_MFPC))
sm->mgmt_frame_prot = 0;
else
sm->mgmt_frame_prot = 1;
+
+ if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection cannot use TKIP");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
@@ -668,8 +786,25 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
"MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
return WPA_INVALID_MDIE;
}
+ } else if (mdie != NULL) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Trying to use non-FT AKM suite, but MDIE included");
+ return WPA_INVALID_AKMP;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_OWE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: No Diffie-Hellman Parameter element");
+ return WPA_INVALID_AKMP;
+ }
+ 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");
+ return WPA_INVALID_AKMP;
+ }
+#endif /* CONFIG_OWE */
sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
if (sm->pairwise < 0)
@@ -723,6 +858,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
+#ifdef CONFIG_SAE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
+ !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for SAE");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_DPP
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKSA cache entry found for DPP");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_DPP */
+
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie);
sm->wpa_ie = os_malloc(wpa_ie_len);
@@ -815,36 +967,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
return 0;
}
-#ifdef CONFIG_PEERKEY
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
- ie->smk = pos + 2 + RSN_SELECTOR_LEN;
- ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
- ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
- ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
- ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
- ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-
- if (pos[1] > RSN_SELECTOR_LEN + 2 &&
- RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
- ie->error = pos + 2 + RSN_SELECTOR_LEN;
- ie->error_len = pos[1] - RSN_SELECTOR_LEN;
- return 0;
- }
-#endif /* CONFIG_PEERKEY */
-
#ifdef CONFIG_IEEE80211W
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
@@ -908,14 +1030,14 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
if (*pos == WLAN_EID_RSN) {
ie->rsn_ie = pos;
ie->rsn_ie_len = pos[1] + 2;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
ie->mdie = pos;
ie->mdie_len = pos[1] + 2;
} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
ie->ftie = pos;
ie->ftie_len = pos[1] + 2;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)
@@ -938,3 +1060,36 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
{
return sm ? sm->mgmt_frame_prot : 0;
}
+
+
+#ifdef CONFIG_OWE
+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)
+{
+ int res;
+ struct wpa_auth_config *conf;
+
+ if (!sm)
+ return pos;
+ conf = &sm->wpa_auth->conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->own_ie_override_len) {
+ if (max_len < conf->own_ie_override_len)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+ conf->own_ie_override, conf->own_ie_override_len);
+ os_memcpy(pos, conf->own_ie_override,
+ conf->own_ie_override_len);
+ return pos + conf->own_ie_override_len;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ res = wpa_write_rsn_ie(conf, pos, max_len,
+ sm->pmksa ? sm->pmksa->pmkid : NULL);
+ if (res < 0)
+ return pos;
+ return pos + res;
+}
+#endif /* CONFIG_OWE */
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index d2067ba3112c..73e433349049 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -19,26 +19,16 @@ struct wpa_eapol_ie_parse {
size_t gtk_len;
const u8 *mac_addr;
size_t mac_addr_len;
-#ifdef CONFIG_PEERKEY
- const u8 *smk;
- size_t smk_len;
- const u8 *nonce;
- size_t nonce_len;
- const u8 *lifetime;
- size_t lifetime_len;
- const u8 *error;
- size_t error_len;
-#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211W
const u8 *igtk;
size_t igtk_len;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
size_t ftie_len;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
const u8 *ip_addr_req;
const u8 *ip_addr_alloc;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 95b40da0f6bb..5ec019971f37 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1064,7 +1064,9 @@ int hostapd_init_wps(struct hostapd_data *hapd,
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
wps->auth_types |= WPS_AUTH_WPA2;
- if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) {
+ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)) {
wps->encr_types |= WPS_ENCR_AES;
wps->encr_types_rsn |= WPS_ENCR_AES;
}
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index e0769c08e764..0b596bbcc631 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -53,12 +53,38 @@ static const struct ieee802_11_parse_test_data parse_tests[] = {
18, ParseOK, 9 },
{ (u8 *) "\x8b\x00", 2, ParseOK, 1 },
{ (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 },
+ { (u8 *) "\xed\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xef\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 },
+ { (u8 *) "\xf0\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xf1\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 },
+ { (u8 *) "\xf2\x00", 2, ParseOK, 1 },
+ { (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
+ { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 },
+ { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55",
+ 15, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11,
+ ParseOK, 1 },
+ { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 },
+ { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 },
+ { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 },
{ NULL, 0, ParseOK, 0 }
};
static int ieee802_11_parse_tests(void)
{
int i, ret = 0;
+ struct wpabuf *buf;
wpa_printf(MSG_INFO, "ieee802_11_parse tests");
@@ -84,6 +110,35 @@ static int ieee802_11_parse_tests(void)
ret = -1;
}
+ buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01",
+ 16, 0x11223344);
+ do {
+ const u8 *pos;
+
+ if (!buf) {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test 2 failed");
+ ret = -1;
+ break;
+ }
+
+ if (wpabuf_len(buf) != 2) {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test 3 failed");
+ ret = -1;
+ break;
+ }
+
+ pos = wpabuf_head(buf);
+ if (pos[0] != 0x01 || pos[1] != 0x02) {
+ wpa_printf(MSG_ERROR,
+ "ieee802_11_vendor_ie_concat test 3 failed");
+ ret = -1;
+ break;
+ }
+ } while (0);
+ wpabuf_free(buf);
+
return ret;
}
diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c
index ebbe6ffdb385..e26407dab902 100644
--- a/src/common/ctrl_iface_common.c
+++ b/src/common/ctrl_iface_common.c
@@ -113,17 +113,53 @@ void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
}
+static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input)
+{
+ const char *value;
+ int val;
+
+ if (!input)
+ return 0;
+
+ value = os_strchr(input, '=');
+ if (!value)
+ return -1;
+ value++;
+ val = atoi(value);
+ if (val < 0 || val > 1)
+ return -1;
+
+ if (str_starts(input, "probe_rx_events=")) {
+ if (val)
+ dst->events |= WPA_EVENT_RX_PROBE_REQUEST;
+ else
+ dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST;
+ }
+
+ return 0;
+}
+
+
int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
- socklen_t fromlen)
+ socklen_t fromlen, const char *input)
{
struct wpa_ctrl_dst *dst;
+ /* Update event registration if already attached */
+ dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+ if (!sockaddr_compare(from, fromlen,
+ &dst->addr, dst->addrlen))
+ return ctrl_set_events(dst, input);
+ }
+
+ /* New attachment */
dst = os_zalloc(sizeof(*dst));
if (dst == NULL)
return -1;
os_memcpy(&dst->addr, from, fromlen);
dst->addrlen = fromlen;
dst->debug_level = MSG_INFO;
+ ctrl_set_events(dst, input);
dl_list_add(ctrl_dst, &dst->list);
sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h
index 0b6e3e740291..85e258e938b6 100644
--- a/src/common/ctrl_iface_common.h
+++ b/src/common/ctrl_iface_common.h
@@ -11,6 +11,9 @@
#include "utils/list.h"
+/* Events enable bits (wpa_ctrl_dst::events) */
+#define WPA_EVENT_RX_PROBE_REQUEST BIT(0)
+
/**
* struct wpa_ctrl_dst - Data structure of control interface monitors
*
@@ -23,13 +26,14 @@ struct wpa_ctrl_dst {
socklen_t addrlen;
int debug_level;
int errors;
+ u32 events; /* WPA_EVENT_* bitmap */
};
void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
socklen_t socklen);
int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
- socklen_t fromlen);
+ socklen_t fromlen, const char *input);
int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
socklen_t fromlen);
int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
diff --git a/src/common/defs.h b/src/common/defs.h
index 4f567945942e..c968cd6cb82a 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - Common definitions
- * Copyright (c) 2004-2015, Jouni Malinen
+ * Copyright (c) 2004-2018, Jouni Malinen
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -51,16 +51,28 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
#define WPA_KEY_MGMT_OSEN BIT(15)
#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
+#define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
+#define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
+#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
+#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
+#define WPA_KEY_MGMT_OWE BIT(22)
+#define WPA_KEY_MGMT_DPP BIT(23)
+#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
WPA_KEY_MGMT_CCKM |
WPA_KEY_MGMT_OSEN |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SUITE_B |
- WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+ WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
}
static inline int wpa_key_mgmt_wpa_psk(int akm)
@@ -76,7 +88,15 @@ static inline int wpa_key_mgmt_ft(int akm)
{
return !!(akm & (WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_FT_SAE));
+ 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_ft_psk(int akm)
+{
+ return !!(akm & WPA_KEY_MGMT_FT_PSK);
}
static inline int wpa_key_mgmt_sae(int akm)
@@ -85,17 +105,32 @@ static inline int wpa_key_mgmt_sae(int akm)
WPA_KEY_MGMT_FT_SAE));
}
+static inline int wpa_key_mgmt_fils(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
static inline int wpa_key_mgmt_sha256(int akm)
{
return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
WPA_KEY_MGMT_IEEE8021X_SHA256 |
+ WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE |
WPA_KEY_MGMT_OSEN |
- WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+ WPA_KEY_MGMT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA256));
}
static inline int wpa_key_mgmt_sha384(int akm)
{
- return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
+ WPA_KEY_MGMT_FILS_SHA384 |
+ WPA_KEY_MGMT_FT_FILS_SHA384));
}
static inline int wpa_key_mgmt_suite_b(int akm)
@@ -108,7 +143,10 @@ static inline int wpa_key_mgmt_wpa(int akm)
{
return wpa_key_mgmt_wpa_ieee8021x(akm) ||
wpa_key_mgmt_wpa_psk(akm) ||
- wpa_key_mgmt_sae(akm);
+ wpa_key_mgmt_fils(akm) ||
+ wpa_key_mgmt_sae(akm) ||
+ akm == WPA_KEY_MGMT_OWE ||
+ akm == WPA_KEY_MGMT_DPP;
}
static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -132,7 +170,13 @@ static inline int wpa_key_mgmt_cckm(int akm)
#define WPA_AUTH_ALG_LEAP BIT(2)
#define WPA_AUTH_ALG_FT BIT(3)
#define WPA_AUTH_ALG_SAE BIT(4)
+#define WPA_AUTH_ALG_FILS BIT(5)
+#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6)
+static inline int wpa_auth_alg_fils(int alg)
+{
+ return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS));
+}
enum wpa_alg {
WPA_ALG_NONE,
@@ -341,4 +385,18 @@ enum wpa_radio_work_band {
BAND_60_GHZ = BIT(2),
};
+enum beacon_rate_type {
+ BEACON_RATE_LEGACY,
+ BEACON_RATE_HT,
+ BEACON_RATE_VHT
+};
+
+enum eap_proxy_sim_state {
+ SIM_STATE_ERROR,
+};
+
+#define OCE_STA BIT(0)
+#define OCE_STA_CFON BIT(1)
+#define OCE_AP BIT(2)
+
#endif /* DEFS_H */
diff --git a/src/common/dhcp.h b/src/common/dhcp.h
new file mode 100644
index 000000000000..e38512c24d9f
--- /dev/null
+++ b/src/common/dhcp.h
@@ -0,0 +1,263 @@
+/*
+ * DHCP definitions
+ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include
+#if __FAVOR_BSD
+#include
+#else
+#define __FAVOR_BSD 1
+#include
+#undef __FAVOR_BSD
+#endif
+
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+struct dhcp_data {
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ be32 xid;
+ be16 secs;
+ be16 flags;
+ be32 client_ip;
+ be32 your_ip;
+ be32 server_ip;
+ be32 relay_ip;
+ u8 hw_addr[16];
+ u8 serv_name[64];
+ u8 boot_file[128];
+} STRUCT_PACKED;
+
+struct bootp_pkt {
+ struct iphdr iph;
+ struct udphdr udph;
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ be32 xid;
+ be16 secs;
+ be16 flags;
+ be32 client_ip;
+ be32 your_ip;
+ be32 server_ip;
+ be32 relay_ip;
+ u8 hw_addr[16];
+ u8 serv_name[64];
+ u8 boot_file[128];
+ u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCP_MAGIC 0x63825363
+
+/*
+ * IANA DHCP/BOOTP registry
+ * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
+*/
+enum dhcp_options {
+ DHCP_OPT_PAD = 0,
+ DHCP_OPT_SUBNET_MASK = 1,
+ DHCP_OPT_TIME_OFFSET = 2,
+ DHCP_OPT_ROUTER = 3,
+ DHCP_OPT_TIME_SERVER = 4,
+ DHCP_OPT_NAME_SERVER = 5,
+ DHCP_OPT_DOMAIN_NAME_SERVER = 6,
+ DHCP_OPT_LOG_SERVER = 7,
+ DHCP_OPT_QUOTES_SERVER = 8,
+ DHCP_OPT_LPR_SERVER = 9,
+ DHCP_OPT_IMPRESS_SERVER = 10,
+ DHCP_OPT_RLP_SERVER = 11,
+ DHCP_OPT_HOSTNAME = 12,
+ DHCP_OPT_BOOT_FILE_SIZE = 13,
+ DHCP_OPT_MERIT_DUMP_FILE = 14,
+ DHCP_OPT_DOMAIN_NAME = 15,
+ DHCP_OPT_SWAP_SERVER = 16,
+ DHCP_OPT_ROOT_PATH = 17,
+ DHCP_OPT_EXTENSION_PATH = 18,
+ DHCP_OPT_FORWARD = 19,
+ DHCP_OPT_SRC_RTE = 20,
+ DHCP_OPT_POLICY_FILTER = 21,
+ DHCP_OPT_MAX_DG_ASSEMBLY = 22,
+ DHCP_OPT_DEFAULT_IP_TTL = 23,
+ DHCP_OPT_MTU_TIMEOUT = 24,
+ DHCP_OPT_MTU_PLATEAU = 25,
+ DHCP_OPT_MTU_INTERFACE = 26,
+ DHCP_OPT_ALL_SUBNETS_LOCAL = 27,
+ DHCP_OPT_BROADCAST_ADDRESS = 28,
+ DHCP_OPT_MASK_DISCOVERY = 29,
+ DHCP_OPT_MASK_SUPPLIER = 30,
+ DHCP_OPT_ROUTER_DISCOVERY = 31,
+ DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32,
+ DHCP_OPT_STATIC_ROUTE = 33,
+ DHCP_OPT_TRAILERS = 34,
+ DHCP_OPT_ARP_TIMEOUT = 35,
+ DHCP_OPT_ETHERNET = 36,
+ DHCP_OPT_TCP_DEFAULT_TTL = 37,
+ DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38,
+ DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39,
+ DHCP_OPT_NIS_DOMAIN = 40,
+ DHCP_OPT_NIS_SERVERS = 41,
+ DHCP_OPT_NTP_SERVERS = 42,
+ DHCP_OPT_VENDOR_SPECIFIC = 43,
+ DHCP_OPT_NETBIOS_NAME_SERVER = 44,
+ DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45,
+ DHCP_OPT_NETBIOS_NODE_TYPE = 46,
+ DHCP_OPT_NETBIOS_SCOPE = 47,
+ DHCP_OPT_FONT_SERVER = 48,
+ DHCP_OPT_DISPLAY_MANAGER = 49,
+ DHCP_OPT_REQUESTED_IP_ADDRESS = 50,
+ DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51,
+ DHCP_OPT_OVERLOAD = 52,
+ DHCP_OPT_MSG_TYPE = 53,
+ DHCP_OPT_SERVER_ID = 54,
+ DHCP_OPT_PARAMETER_REQ_LIST = 55,
+ DHCP_OPT_MESSAGE = 56,
+ DHCP_OPT_MAX_MESSAGE_SIZE = 57,
+ DHCP_OPT_RENEWAL_TIME = 58,
+ DHCP_OPT_REBINDING_TIME = 59,
+ DHCP_OPT_VENDOR_CLASS_ID = 60,
+ DHCP_OPT_CLIENT_ID = 61,
+ DHCP_OPT_NETWARE_IP_DOMAIN = 62,
+ DHCP_OPT_NETWARE_IP_OPTION = 63,
+ DHCP_OPT_NIS_V3_DOMAIN = 64,
+ DHCP_OPT_NIS_V3_SERVERS = 65,
+ DHCP_OPT_TFTP_SERVER_NAME = 66,
+ DHCP_OPT_BOOT_FILE_NAME = 67,
+ DHCP_OPT_HOME_AGENT_ADDRESSES = 68,
+ DHCP_OPT_SMTP_SERVER = 69,
+ DHCP_OPT_POP3_SERVER = 70,
+ DHCP_OPT_NNTP_SERVER = 71,
+ DHCP_OPT_WWW_SERVER = 72,
+ DHCP_OPT_FINGER_SERVER = 73,
+ DHCP_OPT_IRC_SERVER = 74,
+ DHCP_OPT_STREETTALK_SERVER = 75,
+ DHCP_OPT_STDA_SERVER = 76,
+ DHCP_OPT_USER_CLASS = 77,
+ DHCP_OPT_DIRECTORY_AGENT = 78,
+ DHCP_OPT_SERVICE_SCOPE = 79,
+ DHCP_OPT_RAPID_COMMIT = 80,
+ DHCP_OPT_CLIENT_FQDN = 81,
+ DHCP_OPT_RELAY_AGENT_INFO = 82,
+ DHCP_OPT_ISNS = 83,
+ DHCP_OPT_NDS_SERVERS = 85,
+ DHCP_OPT_NDS_TREE_NAME = 86,
+ DHCP_OPT_NDS_CONTEXT = 87,
+ DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88,
+ DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89,
+ DHCP_OPT_AUTHENTICATION = 90,
+ DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91,
+ DHCP_OPT_ASSOCIATED_IP = 92,
+ DHCP_OPT_CLIENT_SYSYEM = 93,
+ DHCP_OPT_CLIENT_NDI = 94,
+ DHCP_OPT_LDAP = 95,
+ DHCP_OPT_UUID_GUID = 97,
+ DHCP_OPT_USER_AUTH = 98,
+ DHCP_OPT_GEOCONF_CIVIC = 99,
+ DHCP_OPT_PCODE = 100,
+ DHCP_OPT_TCODE = 101,
+ DHCP_OPT_NETINFO_ADDRESS = 112,
+ DHCP_OPT_NETINFO_TAG = 113,
+ DHCP_OPT_URL = 114,
+ DHCP_OPT_AUTO_CONFIG = 116,
+ DHCP_OPT_NAME_SERVICE_SEARCH = 117,
+ DHCP_OPT_SUBNET_SELECTION = 118,
+ DHCP_OPT_DOMAIN_SEARCH = 119,
+ DHCP_OPT_SIP_SERVERS_DCP = 120,
+ DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121,
+ DHCP_OPT_CCC = 122,
+ DHCP_OPT_GEOCONF = 123,
+ DHCP_OPT_V_I_VENDOR_CLASS = 124,
+ DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125,
+ DHCP_OPT_PANA_AGENT = 136,
+ DHCP_OPT_V4_LOST = 137,
+ DHCP_OPT_CAPWAP_AC_V4 = 138,
+ DHCP_OPT_IPV4_ADDRESS_MOS = 139,
+ DHCP_OPT_IPV4_FQDN_MOS = 140,
+ DHCP_OPT_SIP_UA_CONF = 141,
+ DHCP_OPT_IPV4_ADDRESS_ANDSF = 142,
+ DHCP_OPT_GEOLOC = 144,
+ DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145,
+ DHCP_OPT_RDNSS_SELECTION = 146,
+ DHCP_OPT_TFTP_SERVER_ADDRESS = 150,
+ DHCP_OPT_STATUS_CODE = 151,
+ DHCP_OPT_BASE_TIME = 152,
+ DHCP_OPT_START_TIME_OF_STATE = 153,
+ DHCP_OPT_QUERY_START_TIME = 154,
+ DHCP_OPT_QUERY_END_TIME = 155,
+ DHCP_OPT_STATE = 156,
+ DHCP_OPT_DATA_SOURCE = 157,
+ DHCP_OPT_V4_PCP_SERVER = 158,
+ DHCP_OPT_V4_PORTPARAMS = 159,
+ DHCP_OPT_CAPTIVE_PORTAL = 160,
+ DHCP_OPT_CONF_FILE = 209,
+ DHCP_OPT_PATH_PREFIX = 210,
+ DHCP_OPT_REBOOT_TIME = 211,
+ DHCP_OPT_6RD = 212,
+ DHCP_OPT_V4_ACCESS_DOMAIN = 213,
+ DHCP_OPT_SUBNET_ALLOCATION = 220,
+ DHCP_OPT_VSS = 221,
+ DHCP_OPT_END = 255
+};
+
+enum dhcp_message_types {
+ DHCPDISCOVER = 1,
+ DHCPOFFER = 2,
+ DHCPREQUEST = 3,
+ DHCPDECLINE = 4,
+ DHCPACK = 5,
+ DHCPNAK = 6,
+ DHCPRELEASE = 7,
+ DHCPINFORM = 8,
+ DHCPFORCERENEW = 9,
+ DHCPLEASEQUERY = 10,
+ DHCPLEASEUNASSIGNED = 11,
+ DHCPLEASEUNKNOWN = 12,
+ DHCPLEASEACTIVE = 13,
+ DHCPBULKLEASEQUERY = 14,
+ DHCPLEASEQUERYDONE = 15,
+ DHCPACTIVELEASEQUERY = 16,
+ DHCPLEASEQUERYSTATUS = 17,
+ DHCPTLS = 18,
+};
+
+enum dhcp_relay_agent_suboptions {
+ DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1,
+ DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2,
+ DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4,
+ DHCP_RELAY_OPT_LINK_SELECTION = 5,
+ DHCP_RELAY_OPT_SUBSCRIBE_ID = 6,
+ DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7,
+ DHCP_RELAY_OPT_AUTHENTICATION = 8,
+ DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9,
+ DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10,
+ DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11,
+ DHCP_RELAY_OPT_RELAY_AGENT_ID = 12,
+ DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13,
+ DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14,
+ DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15,
+ DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16,
+ DHCP_RELAY_OPT_OPERATOR_ID = 17,
+ DHCP_RELAY_OPT_OPERATOR_REALM = 18,
+ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151,
+ DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152,
+};
+
+enum access_technology_types {
+ ACCESS_TECHNOLOGY_VIRTUAL = 1,
+ ACCESS_TECHNOLOGY_PPP = 2,
+ ACCESS_TECHNOLOGY_ETHERNET = 3,
+ ACCESS_TECHNOLOGY_WLAN = 4,
+ ACCESS_TECHNOLOGY_WIMAX = 5,
+};
+
+#endif /* DHCP_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
new file mode 100644
index 000000000000..e715e0454d72
--- /dev/null
+++ b/src/common/dpp.c
@@ -0,0 +1,7691 @@
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include
+#include
+#include
+#include
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "drivers/driver.h"
+#include "dpp.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+
+static int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ sig->r = r;
+ sig->s = s;
+ return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+ const BIGNUM **ps)
+{
+ if (pr)
+ *pr = sig->r;
+ if (ps)
+ *ps = sig->s;
+}
+
+#endif
+
+
+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. */
+ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+ { NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
+};
+static const u8 pkex_resp_x_p384[48] = {
+ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+ 0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+ 0x03, 0xa8
+};
+static const u8 pkex_resp_x_p521[66] = {
+ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+ 0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+ 0xce, 0xe1
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
+};
+
+
+static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *ctx;
+ char *x_str = NULL, *y_str = NULL;
+
+ if (!wpa_debug_show_keys)
+ return;
+
+ ctx = BN_CTX_new();
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+ BN_CTX_free(ctx);
+}
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (curve->hash_len == 32)
+ return sha256_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 48)
+ return sha384_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 64)
+ return sha512_vector(num_elem, addr, len, mac);
+ return -1;
+}
+
+
+static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen)
+{
+ if (hash_len == 32)
+ return hmac_sha256_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 48)
+ return hmac_sha384_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 64)
+ return hmac_sha512_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ return -1;
+}
+
+
+static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 48)
+ return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 64)
+ return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+ mac);
+ return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256(key, key_len, data, data_len, mac);
+ if (hash_len == 48)
+ return hmac_sha384(key, key_len, data, data_len, mac);
+ if (hash_len == 64)
+ return hmac_sha512(key, key_len, data, data_len, mac);
+ return -1;
+}
+
+
+static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+ int num_bytes, offset;
+
+ num_bytes = BN_num_bytes(bn);
+ if ((size_t) num_bytes > len)
+ return -1;
+ offset = len - num_bytes;
+ os_memset(pos, 0, offset);
+ BN_bn2bin(bn, pos + offset);
+ return 0;
+}
+
+
+static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+ int len, res;
+ EC_KEY *eckey;
+ struct wpabuf *buf;
+ unsigned char *pos;
+
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (!eckey)
+ return NULL;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+ len = i2o_ECPublicKey(eckey, NULL);
+ if (len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to determine public key encoding length");
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, len);
+ res = i2o_ECPublicKey(eckey, &pos);
+ EC_KEY_free(eckey);
+ if (res != len) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to encode public key (res=%d/%d)",
+ res, len);
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (!prefix) {
+ /* Remove 0x04 prefix to match DPP definition */
+ pos = wpabuf_mhead(buf);
+ os_memmove(pos, pos + 1, len - 1);
+ buf->used--;
+ }
+
+ return buf;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len)
+{
+ EC_KEY *eckey = NULL;
+ BN_CTX *ctx;
+ EC_POINT *point = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ ctx = BN_CTX_new();
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ return NULL;
+ }
+
+ point = EC_POINT_new(group);
+ x = BN_bin2bn(buf_x, len, NULL);
+ y = BN_bin2bn(buf_y, len, NULL);
+ if (!point || !x || !y) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ goto fail;
+ }
+
+ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx) ||
+ EC_POINT_is_at_infinity(group, point)) {
+ wpa_printf(MSG_ERROR, "DPP: Invalid point");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ BN_free(x);
+ BN_free(y);
+ EC_KEY_free(eckey);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ return pkey;
+fail:
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+}
+
+
+static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
+ const u8 *buf, size_t len)
+{
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ if (len & 1)
+ return NULL;
+
+ eckey = EVP_PKEY_get1_EC_KEY(group_key);
+ if (!eckey) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Could not get EC_KEY from group_key");
+ return NULL;
+ }
+
+ group = EC_KEY_get0_group(eckey);
+ if (group)
+ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+ len / 2);
+ else
+ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+ EC_KEY_free(eckey);
+ return pkey;
+}
+
+
+static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+{
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
+ size_t len)
+{
+ struct wpabuf *msg;
+
+ msg = wpabuf_alloc(8 + len);
+ if (!msg)
+ return NULL;
+ wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_be24(msg, OUI_WFA);
+ wpabuf_put_u8(msg, DPP_OUI_TYPE);
+ wpabuf_put_u8(msg, 1); /* Crypto Suite */
+ wpabuf_put_u8(msg, type);
+ return msg;
+}
+
+
+const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
+{
+ u16 id, alen;
+ const u8 *pos = buf, *end = buf + len;
+
+ while (end - pos >= 4) {
+ id = WPA_GET_LE16(pos);
+ pos += 2;
+ alen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (alen > end - pos)
+ return NULL;
+ if (id == req_id) {
+ *ret_len = alen;
+ return pos;
+ }
+ pos += alen;
+ }
+
+ return NULL;
+}
+
+
+int dpp_check_attrs(const u8 *buf, size_t len)
+{
+ const u8 *pos, *end;
+ int wrapped_data = 0;
+
+ pos = buf;
+ end = buf + len;
+ while (end - pos >= 4) {
+ u16 id, alen;
+
+ id = WPA_GET_LE16(pos);
+ pos += 2;
+ alen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
+ id, alen);
+ if (alen > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Truncated message - not enough room for the attribute - dropped");
+ return -1;
+ }
+ if (wrapped_data) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: An unexpected attribute included after the Wrapped Data attribute");
+ return -1;
+ }
+ if (id == DPP_ATTR_WRAPPED_DATA)
+ wrapped_data = 1;
+ pos += alen;
+ }
+
+ if (end != pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected octets (%d) after the last attribute",
+ (int) (end - pos));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
+{
+ if (!info)
+ return;
+ os_free(info->uri);
+ os_free(info->info);
+ EVP_PKEY_free(info->pubkey);
+ os_free(info);
+}
+
+
+const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
+{
+ switch (type) {
+ case DPP_BOOTSTRAP_QR_CODE:
+ return "QRCODE";
+ case DPP_BOOTSTRAP_PKEX:
+ return "PKEX";
+ }
+ return "??";
+}
+
+
+static int dpp_uri_valid_info(const char *info)
+{
+ while (*info) {
+ unsigned char val = *info++;
+
+ if (val < 0x20 || val > 0x7e || val == 0x3b)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
+{
+ bi->uri = os_strdup(uri);
+ return bi->uri ? 0 : -1;
+}
+
+
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+ const char *chan_list)
+{
+ const char *pos = chan_list;
+ int opclass, channel, freq;
+
+ while (pos && *pos && *pos != ';') {
+ opclass = atoi(pos);
+ if (opclass <= 0)
+ goto fail;
+ pos = os_strchr(pos, '/');
+ if (!pos)
+ goto fail;
+ pos++;
+ channel = atoi(pos);
+ if (channel <= 0)
+ goto fail;
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ freq = ieee80211_chan_to_freq(NULL, opclass, channel);
+ wpa_printf(MSG_DEBUG,
+ "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
+ opclass, channel, freq);
+ if (freq < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
+ opclass, channel);
+ } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too many channels in URI channel-list - ignore list");
+ bi->num_freq = 0;
+ break;
+ } else {
+ bi->freq[bi->num_freq++] = freq;
+ }
+
+ if (*pos == ';' || *pos == '\0')
+ break;
+ if (*pos != ',')
+ goto fail;
+ pos++;
+ }
+
+ return 0;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
+ return -1;
+}
+
+
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
+{
+ if (!mac)
+ return 0;
+
+ if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
+
+ return 0;
+}
+
+
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
+{
+ const char *end;
+
+ if (!info)
+ return 0;
+
+ end = os_strchr(info, ';');
+ if (!end)
+ end = info + os_strlen(info);
+ bi->info = os_malloc(end - info + 1);
+ if (!bi->info)
+ return -1;
+ os_memcpy(bi->info, info, end - info);
+ bi->info[end - info] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
+ if (!dpp_uri_valid_info(bi->info)) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+ ASN1_OBJECT *oid;
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+ if (oid && OBJ_cmp(poid, oid) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+ int i, tmp;
+
+ if (!nid)
+ return NULL;
+ for (i = 0; dpp_curves[i].name; i++) {
+ tmp = OBJ_txt2nid(dpp_curves[i].name);
+ if (tmp == nid)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
+{
+ const char *end;
+ u8 *data;
+ size_t data_len;
+ EVP_PKEY *pkey;
+ const unsigned char *p;
+ int res;
+ X509_PUBKEY *pub = NULL;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int ppklen;
+ X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ ASN1_OBJECT *pa_oid;
+#else
+ const ASN1_OBJECT *pa_oid;
+#endif
+ const void *pval;
+ int ptype;
+ const ASN1_OBJECT *poid;
+ char buf[100];
+
+ end = os_strchr(info, ';');
+ if (!end)
+ return -1;
+
+ data = base64_decode((const unsigned char *) info, end - info,
+ &data_len);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Invalid base64 encoding on URI public-key");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
+ data, data_len);
+
+ if (sha256_vector(1, (const u8 **) &data, &data_len,
+ bi->pubkey_hash) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ os_free(data);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+ bi->pubkey_hash, SHA256_MAC_LEN);
+
+ /* DER encoded ASN.1 SubjectPublicKeyInfo
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ *
+ * subjectPublicKey = compressed format public key per ANSI X9.63
+ * algorithm = ecPublicKey (1.2.840.10045.2.1)
+ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+ * prime256v1 (1.2.840.10045.3.1.7)
+ */
+
+ p = data;
+ pkey = d2i_PUBKEY(NULL, &p, data_len);
+ os_free(data);
+
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo does not describe an EC key");
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+
+ res = X509_PUBKEY_set(&pub, pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+ goto fail;
+ }
+
+ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters");
+ goto fail;
+ }
+ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+
+ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+ if (ptype != V_ASN1_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+ goto fail;
+ }
+ poid = pval;
+ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+ bi->curve = dpp_get_curve_oid(poid);
+ if (!bi->curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+ buf);
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+ X509_PUBKEY_free(pub);
+ bi->pubkey = pkey;
+ return 0;
+fail:
+ X509_PUBKEY_free(pub);
+ EVP_PKEY_free(pkey);
+ return -1;
+}
+
+
+static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
+{
+ const char *pos = uri;
+ const char *end;
+ const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+ struct dpp_bootstrap_info *bi;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
+
+ if (os_strncmp(pos, "DPP:", 4) != 0) {
+ wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
+ return NULL;
+ }
+ pos += 4;
+
+ for (;;) {
+ end = os_strchr(pos, ';');
+ if (!end)
+ break;
+
+ if (end == pos) {
+ /* Handle terminating ";;" and ignore unexpected ";"
+ * for parsing robustness. */
+ pos++;
+ continue;
+ }
+
+ if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
+ chan_list = pos + 2;
+ else if (pos[0] == 'M' && pos[1] == ':' && !mac)
+ mac = pos + 2;
+ else if (pos[0] == 'I' && pos[1] == ':' && !info)
+ info = pos + 2;
+ else if (pos[0] == 'K' && pos[1] == ':' && !pk)
+ pk = pos + 2;
+ else
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: Ignore unrecognized URI parameter",
+ pos, end - pos);
+ pos = end + 1;
+ }
+
+ if (!pk) {
+ wpa_printf(MSG_INFO, "DPP: URI missing public-key");
+ return NULL;
+ }
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+
+ if (dpp_clone_uri(bi, uri) < 0 ||
+ dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
+ dpp_parse_uri_mac(bi, mac) < 0 ||
+ dpp_parse_uri_info(bi, info) < 0 ||
+ dpp_parse_uri_pk(bi, pk) < 0) {
+ dpp_bootstrap_info_free(bi);
+ bi = NULL;
+ }
+
+ return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_parse_uri(uri);
+ if (bi)
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ return bi;
+}
+
+
+static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+ EC_KEY *eckey;
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ return;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (group && point)
+ dpp_debug_print_point(title, group, point);
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+ OPENSSL_free(der);
+ if (der_len <= 0) {
+ der = NULL;
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+ OPENSSL_free(der);
+ }
+
+ EC_KEY_free(eckey);
+}
+
+
+static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+ EVP_PKEY_CTX *kctx = NULL;
+ EC_KEY *ec_params;
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+ nid = OBJ_txt2nid(curve->name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+ return NULL;
+ }
+
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+ goto fail;
+ }
+
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
+
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return key;
+fail:
+ EVP_PKEY_CTX_free(kctx);
+ EVP_PKEY_free(params);
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_name(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+ (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_jwk_crv(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return NULL;
+ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ group = EC_KEY_get0_group(eckey);
+ if (!group) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ nid = EC_GROUP_get_curve_name(group);
+ *curve = dpp_get_curve_nid(nid);
+ if (!*curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+ nid);
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ return pkey;
+}
+
+
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ EC_KEY *eckey;
+ struct wpabuf *ret = NULL;
+ size_t len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+ int nid;
+
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
+ dpp_debug_print_point("DPP: bootstrap public key", group, point);
+ nid = EC_GROUP_get_curve_name(group);
+
+ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+ if (!bootstrap ||
+ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(bootstrap->pub_key->data);
+ bootstrap->pub_key->data = der;
+ der = NULL;
+ bootstrap->pub_key->length = len;
+ /* No unused bits */
+ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
+ OPENSSL_free(der);
+ EC_KEY_free(eckey);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *der;
+ int res;
+ const u8 *addr[1];
+ size_t len[1];
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ return -1;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ addr[0] = wpabuf_head(der);
+ len[0] = wpabuf_len(der);
+ res = sha256_vector(1, addr, len, bi->pubkey_hash);
+ if (res < 0)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ else
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+ SHA256_MAC_LEN);
+ wpabuf_free(der);
+ return res;
+}
+
+
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ unsigned char *base64 = NULL;
+ char *pos, *end;
+ size_t len;
+ struct wpabuf *der = NULL;
+ const u8 *addr[1];
+ int res;
+
+ if (!curve) {
+ bi->curve = &dpp_curves[0];
+ } else {
+ bi->curve = dpp_get_curve_name(curve);
+ if (!bi->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+ curve);
+ return NULL;
+ }
+ }
+ if (privkey)
+ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+ else
+ bi->pubkey = dpp_gen_keypair(bi->curve);
+ if (!bi->pubkey)
+ goto fail;
+ bi->own = 1;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ addr[0] = wpabuf_head(der);
+ len = wpabuf_len(der);
+ res = sha256_vector(1, addr, &len, bi->pubkey_hash);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+ SHA256_MAC_LEN);
+
+ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+ wpabuf_free(der);
+ der = NULL;
+ if (!base64)
+ goto fail;
+ pos = (char *) base64;
+ end = pos + len;
+ for (;;) {
+ pos = os_strchr(pos, '\n');
+ if (!pos)
+ break;
+ os_memmove(pos, pos + 1, end - pos);
+ }
+ return (char *) base64;
+fail:
+ os_free(base64);
+ wpabuf_free(der);
+ return NULL;
+}
+
+
+static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1,
+ unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "first intermediate key";
+ int res;
+
+ /* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+ /* HKDF-Extract(<>, M.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+ k1, hash_len);
+ return 0;
+}
+
+
+static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2,
+ unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "second intermediate key";
+ int res;
+
+ /* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+ k2, hash_len);
+ return 0;
+}
+
+
+static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
+ unsigned int hash_len)
+{
+ size_t nonce_len;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+ const char *info_ke = "DPP Key";
+ u8 prk[DPP_MAX_HASH_LEN];
+ int res;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem = 0;
+
+ if (!auth->Mx_len || !auth->Nx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mx/Nx not available - cannot derive ke");
+ return -1;
+ }
+
+ /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */
+
+ /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+ nonce_len = auth->curve->nonce_len;
+ os_memcpy(nonces, auth->i_nonce, nonce_len);
+ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+ addr[num_elem] = auth->Mx;
+ len[num_elem] = auth->Mx_len;
+ num_elem++;
+ addr[num_elem] = auth->Nx;
+ len[num_elem] = auth->Nx_len;
+ num_elem++;
+ if (auth->peer_bi && auth->own_bi) {
+ if (!auth->Lx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Lx not available - cannot derive ke");
+ return -1;
+ }
+ addr[num_elem] = auth->Lx;
+ len[num_elem] = auth->secret_len;
+ num_elem++;
+ }
+ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+ num_elem, addr, len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)",
+ ke, hash_len);
+ return 0;
+}
+
+
+static void dpp_build_attr_status(struct wpabuf *msg,
+ enum dpp_status_error status)
+{
+ wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
+ wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, status);
+}
+
+
+static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+ const struct wpabuf *pi,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ unsigned int neg_freq)
+{
+ struct wpabuf *msg;
+ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+ u8 *pos;
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end;
+
+ /* Build DPP Authentication Request frame attributes */
+ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+ 4 + sizeof(wrapped_data);
+ if (neg_freq > 0)
+ attr_len += 4 + 2;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Initiator Protocol Key */
+ if (pi) {
+ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pi));
+ wpabuf_put_buf(msg, pi);
+ }
+
+ /* Channel */
+ if (neg_freq > 0) {
+ u8 op_class, channel;
+
+ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+ &channel) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported negotiation frequency request: %d",
+ neg_freq);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_u8(msg, op_class);
+ wpabuf_put_u8(msg, channel);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({I-nonce, I-capabilities}k1) */
+ pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ goto skip_i_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len - 1);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+ pos += nonce_len - 1;
+ goto skip_i_nonce;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len);
+ pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+ goto skip_i_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->i_capab = auth->allowed_roles;
+ *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+ pos[-1] = 0;
+ }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* 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 */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Request frame attributes", msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+ enum dpp_status_error status,
+ const struct wpabuf *pr,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ const u8 *r_nonce, const u8 *i_nonce,
+ const u8 *wrapped_r_auth,
+ size_t wrapped_r_auth_len,
+ const u8 *siv_key)
+{
+ struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end, *pos;
+
+ auth->waiting_auth_conf = 1;
+ auth->auth_resp_tries = 0;
+
+ /* 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_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ if (status != 255)
+ dpp_build_attr_status(msg, status);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Responder Protocol Key */
+ if (pr) {
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+ }
+
+ attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+ pos = clear;
+
+ if (r_nonce) {
+ /* R-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, r_nonce, nonce_len);
+ pos += nonce_len;
+ }
+
+ if (i_nonce) {
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+ pos[nonce_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ pos += nonce_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+ goto skip_r_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* R-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+ DPP_CAPAB_ENROLLEE;
+ *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+ pos[-1] = 0;
+ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - incompatible R-capabilities");
+ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+ pos[-1] = 0;
+ else
+ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+ DPP_CAPAB_CONFIGURATOR;
+ }
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wrapped_r_auth) {
+ /* {R-auth}ke */
+ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+ pos += 2;
+ WPA_PUT_LE16(pos, wrapped_r_auth_len);
+ pos += 2;
+ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+ pos += wrapped_r_auth_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 */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Response frame attributes", msg);
+ return msg;
+}
+
+
+static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
+ u16 num_modes, unsigned int freq)
+{
+ u16 m;
+ int c, flag;
+
+ if (!own_modes || !num_modes)
+ return 1;
+
+ for (m = 0; m < num_modes; m++) {
+ for (c = 0; c < own_modes[m].num_channels; c++) {
+ if ((unsigned int) own_modes[m].channels[c].freq !=
+ freq)
+ continue;
+ flag = own_modes[m].channels[c].flag;
+ if (!(flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR)))
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
+ return 0;
+}
+
+
+static int freq_included(const unsigned int freqs[], unsigned int num,
+ unsigned int freq)
+{
+ while (num > 0) {
+ if (freqs[--num] == freq)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void freq_to_start(unsigned int freqs[], unsigned int num,
+ unsigned int freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < num; i++) {
+ if (freqs[i] == freq)
+ break;
+ }
+ if (i == 0 || i >= num)
+ return;
+ os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
+ freqs[0] = freq;
+}
+
+
+static int dpp_channel_intersect(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
+ unsigned int i, freq;
+
+ for (i = 0; i < peer_bi->num_freq; i++) {
+ freq = peer_bi->freq[i];
+ if (freq_included(auth->freq, auth->num_freq, freq))
+ continue;
+ if (dpp_channel_ok_init(own_modes, num_modes, freq))
+ auth->freq[auth->num_freq++] = freq;
+ }
+ if (!auth->num_freq) {
+ wpa_printf(MSG_INFO,
+ "DPP: No available channels for initiating DPP Authentication");
+ return -1;
+ }
+ auth->curr_freq = auth->freq[0];
+ return 0;
+}
+
+
+static int dpp_channel_local_list(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ u16 m;
+ int c, flag;
+ unsigned int freq;
+
+ auth->num_freq = 0;
+
+ if (!own_modes || !num_modes) {
+ auth->freq[0] = 2412;
+ auth->freq[1] = 2437;
+ auth->freq[2] = 2462;
+ auth->num_freq = 3;
+ return 0;
+ }
+
+ for (m = 0; m < num_modes; m++) {
+ for (c = 0; c < own_modes[m].num_channels; c++) {
+ freq = own_modes[m].channels[c].freq;
+ flag = own_modes[m].channels[c].flag;
+ if (flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+ if (freq_included(auth->freq, auth->num_freq, freq))
+ continue;
+ auth->freq[auth->num_freq++] = freq;
+ if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+ m = num_modes;
+ break;
+ }
+ }
+ }
+
+ return auth->num_freq == 0 ? -1 : 0;
+}
+
+
+static int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ int res;
+ char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
+ unsigned int i;
+
+ if (auth->peer_bi->num_freq > 0)
+ res = dpp_channel_intersect(auth, own_modes, num_modes);
+ else
+ res = dpp_channel_local_list(auth, own_modes, num_modes);
+ if (res < 0)
+ return res;
+
+ /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
+ * likely channels first. */
+ freq_to_start(auth->freq, auth->num_freq, 2462);
+ freq_to_start(auth->freq, auth->num_freq, 2412);
+ freq_to_start(auth->freq, auth->num_freq, 2437);
+
+ auth->freq_idx = 0;
+ auth->curr_freq = auth->freq[0];
+
+ pos = freqs;
+ end = pos + sizeof(freqs);
+ for (i = 0; i < auth->num_freq; i++) {
+ res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
+ freqs);
+
+ return 0;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+ struct dpp_bootstrap_info *bi;
+ char *pk = NULL;
+ size_t len;
+
+ if (auth->own_bi)
+ return 0; /* already generated */
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return -1;
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
+ if (!pk)
+ goto fail;
+
+ len = 4; /* "DPP:" */
+ len += 4 + os_strlen(pk);
+ bi->uri = os_malloc(len + 1);
+ if (!bi->uri)
+ goto fail;
+ os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Auto-generated own bootstrapping key info: URI %s",
+ bi->uri);
+
+ auth->tmp_own_bi = auth->own_bi = bi;
+
+ os_free(pk);
+
+ return 0;
+fail:
+ os_free(pk);
+ dpp_bootstrap_info_free(bi);
+ return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_authentication *auth;
+ size_t nonce_len;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ struct wpabuf *pi = NULL;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ return NULL;
+ auth->msg_ctx = msg_ctx;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = dpp_allowed_roles;
+ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = peer_bi->curve;
+
+ if (dpp_autogen_bootstrap_key(auth) < 0 ||
+ dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pi)
+ goto fail;
+
+ /* ECDH: M = pI * BR */
+ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ auth->secret_len = secret_len;
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+ wpabuf_free(pi);
+ pi = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+ wpabuf_free(pi);
+ pi = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+ i_pubkey_hash, neg_freq);
+ if (!auth->req_msg)
+ goto fail;
+
+out:
+ wpabuf_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+ const char *json)
+{
+ size_t nonce_len;
+ size_t json_len, clear_len;
+ struct wpabuf *clear = NULL, *msg = NULL;
+ u8 *wrapped;
+ size_t attr_len;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
+
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->e_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
+ json_len = os_strlen(json);
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+
+ /* { E-nonce, configAttrib }ke */
+ clear_len = 4 + nonce_len + 4 + json_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = wpabuf_alloc(attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, nonce_len - 1);
+ wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* 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);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+ if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
+ goto skip_conf_attr_obj;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* configAttrib */
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
+ wpabuf_put_le16(clear, json_len);
+ wpabuf_put_data(clear, json, json_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_conf_attr_obj:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ 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);
+
+ /* No AES-SIV AD */
+ 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),
+ 0, NULL, NULL, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Configuration Request frame attributes", msg);
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(clear);
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication success - clear temporary keys");
+ os_memset(auth->Mx, 0, sizeof(auth->Mx));
+ auth->Mx_len = 0;
+ os_memset(auth->Nx, 0, sizeof(auth->Nx));
+ auth->Nx_len = 0;
+ os_memset(auth->Lx, 0, sizeof(auth->Lx));
+ auth->Lx_len = 0;
+ os_memset(auth->k1, 0, sizeof(auth->k1));
+ os_memset(auth->k2, 0, sizeof(auth->k2));
+
+ auth->auth_success = 1;
+}
+
+
+static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+ struct wpabuf *pix, *prx, *bix, *brx;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 zero = 0;
+ int res = -1;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ addr[num_elem] = &zero;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 one = 1;
+ int res = -1;
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->peer_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->own_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = &one;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL;
+ EC_KEY *BI = NULL, *bR = NULL, *pR = NULL;
+ const EC_POINT *BI_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx, *sum, *q;
+ const BIGNUM *bR_bn, *pR_bn;
+ int ret = -1;
+
+ /* L = ((bR + pR) modulo q) * BI */
+
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ q = BN_new();
+ lx = BN_new();
+ if (!bnctx || !sum || !q || !lx)
+ goto fail;
+ BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+ if (!BI)
+ goto fail;
+ BI_point = EC_KEY_get0_public_key(BI);
+ group = EC_KEY_get0_group(BI);
+ if (!group)
+ goto fail;
+
+ bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+ pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key);
+ if (!bR || !pR)
+ goto fail;
+ bR_bn = EC_KEY_get0_private_key(bR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!bR_bn || !pR_bn)
+ goto fail;
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+ goto fail;
+ l = EC_POINT_new(group);
+ if (!l ||
+ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_KEY_free(BI);
+ EC_KEY_free(bR);
+ EC_KEY_free(pR);
+ BN_clear_free(lx);
+ BN_clear_free(sum);
+ BN_free(q);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL, *sum = NULL;
+ EC_KEY *bI = NULL, *BR = NULL, *PR = NULL;
+ const EC_POINT *BR_point, *PR_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx;
+ const BIGNUM *bI_bn;
+ int ret = -1;
+
+ /* L = bI * (BR + PR) */
+
+ bnctx = BN_CTX_new();
+ lx = BN_new();
+ if (!bnctx || !lx)
+ goto fail;
+ BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey);
+ PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key);
+ if (!BR || !PR)
+ goto fail;
+ BR_point = EC_KEY_get0_public_key(BR);
+ PR_point = EC_KEY_get0_public_key(PR);
+
+ bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey);
+ if (!bI)
+ goto fail;
+ group = EC_KEY_get0_group(bI);
+ bI_bn = EC_KEY_get0_private_key(bI);
+ if (!group || !bI_bn)
+ goto fail;
+ sum = EC_POINT_new(group);
+ l = EC_POINT_new(group);
+ if (!sum || !l ||
+ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_POINT_clear_free(sum);
+ EC_KEY_free(bI);
+ EC_KEY_free(BR);
+ EC_KEY_free(PR);
+ BN_clear_free(lx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
+{
+ size_t nonce_len;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ struct wpabuf *msg, *pr = NULL;
+ u8 r_auth[4 + DPP_MAX_HASH_LEN];
+ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
+ size_t wrapped_r_auth_len;
+ int ret = -1;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+ enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+ if (!auth->own_bi)
+ return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ /* ECDH: N = pR * PI */
+ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ if (auth->own_bi && auth->peer_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_responder(auth) < 0)
+ goto fail;
+ }
+
+ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+ goto fail;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ r_auth, 4 + auth->curve->hash_len,
+ 0, NULL, NULL, wrapped_r_auth) < 0)
+ goto fail;
+ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+ wrapped_r_auth, wrapped_r_auth_len);
+ w_r_auth = wrapped_r_auth;
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+ r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+ wpabuf_free(pr);
+ pr = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+ wpabuf_free(pr);
+ pr = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+ goto fail;
+ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+ w_r_auth = NULL;
+ wrapped_r_auth_len = 0;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+ r_nonce = NULL;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ r_nonce, i_nonce,
+ w_r_auth, wrapped_r_auth_len,
+ auth->k2);
+ if (!msg)
+ goto fail;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ ret = 0;
+fail:
+ wpabuf_free(pr);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->own_bi)
+ return -1;
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ NULL, i_nonce, NULL, 0, auth->k1);
+ if (!msg)
+ return -1;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ return 0;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ unsigned int freq, const u8 *hdr, const u8 *attr_start,
+ size_t attr_len)
+{
+ EVP_PKEY *pi = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+ *channel;
+ 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_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ 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) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ auth = os_zalloc(sizeof(*auth));
+ if (!auth)
+ goto fail;
+ auth->msg_ctx = msg_ctx;
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = own_bi->curve;
+ auth->curr_freq = freq;
+
+ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+ &channel_len);
+ if (channel) {
+ int neg_freq;
+
+ if (channel_len < 2) {
+ dpp_auth_fail(auth, "Too short Channel attribute");
+ goto fail;
+ }
+
+ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+ channel[0], channel[1], neg_freq);
+ if (neg_freq < 0) {
+ dpp_auth_fail(auth,
+ "Unsupported Channel attribute value");
+ goto fail;
+ }
+
+ if (auth->curr_freq != (unsigned int) neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Changing negotiation channel from %u MHz to %u MHz",
+ freq, neg_freq);
+ auth->curr_freq = neg_freq;
+ }
+ }
+
+ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+ &i_proto_len);
+ if (!i_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Initiator Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+ i_proto, i_proto_len);
+
+ /* M = bR * PI */
+ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+ if (!pi) {
+ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+ ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pi) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ auth->secret_len = secret_len;
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ 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->k1, 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;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+ i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_I_CAPABILITIES,
+ &i_capab_len);
+ if (!i_capab || i_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
+ goto fail;
+ }
+ auth->i_capab = i_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ unwrapped = NULL;
+
+ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+ case DPP_CAPAB_ENROLLEE:
+ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ break;
+ case DPP_CAPAB_CONFIGURATOR:
+ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Enrollee role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ break;
+ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator/Enrollee role");
+ goto not_compatible;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+ auth->i_capab & DPP_CAPAB_ROLE_MASK);
+ goto fail;
+ }
+
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+ if (dpp_auth_build_resp_status(auth,
+ DPP_STATUS_RESPONSE_PENDING) < 0)
+ goto fail;
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+ auth->response_pending = 1;
+ os_memcpy(auth->waiting_pubkey_hash,
+ i_bootstrap, i_bootstrap_len);
+ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+ i_bootstrap_len);
+ } else {
+ hex[0] = '\0';
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+ "%s", hex);
+ return auth;
+ }
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ goto fail;
+
+ return auth;
+
+not_compatible:
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "i-capab=0x%02x", auth->i_capab);
+ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+ auth->configurator = 1;
+ else
+ auth->configurator = 0;
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+ goto fail;
+
+ auth->remove_on_tx_status = 1;
+ return auth;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ EVP_PKEY_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ dpp_auth_deinit(auth);
+ return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+ struct dpp_bootstrap_info *peer_bi)
+{
+ if (!auth || !auth->response_pending ||
+ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+ MACSTR, MAC2STR(auth->peer_mac_addr));
+ auth->peer_bi = peer_bi;
+
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return -1;
+
+ return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ u8 i_auth[4 + DPP_MAX_HASH_LEN];
+ size_t i_auth_len;
+ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+ size_t r_nonce_len;
+ const u8 *addr[2];
+ size_t len[2], attr_len;
+ u8 *wrapped_i_auth;
+ u8 *wrapped_r_nonce;
+ u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+ i_auth_len = 4 + auth->curve->hash_len;
+ r_nonce_len = 4 + auth->curve->nonce_len;
+ /* Build DPP Authentication Confirmation frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ if (auth->own_bi)
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+ goto skip_wrapped_data;
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* 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 */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (status == DPP_STATUS_OK) {
+ /* I-auth wrapped with ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+ * 1) */
+ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ i_auth, i_auth_len,
+ 2, addr, len, wrapped_i_auth) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ } else {
+ /* R-nonce wrapped with k2 */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+ r_nonce, r_nonce_len,
+ 2, addr, len, wrapped_r_nonce) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Confirmation frame attributes",
+ msg);
+ if (status == DPP_STATUS_OK)
+ dpp_auth_success(auth);
+
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data, u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *i_nonce, *r_capab;
+ u16 i_nonce_len, r_capab_len;
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported incompatible roles");
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported more time needed");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported failure (status %d)",
+ status);
+ dpp_auth_fail(auth, "Responder reported failure");
+ return;
+ }
+
+ 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->k1, 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;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "r-capab=0x%02x", auth->r_capab);
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+ role);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue waiting for full DPP Authentication Response");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_RESPONSE_PENDING "%s",
+ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+ }
+ }
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ EVP_PKEY *pr;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL, *unwrapped2 = NULL;
+ size_t unwrapped_len = 0, unwrapped2_len = 0;
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+ wrapped2_len, r_auth_len;
+ u8 r_auth2[DPP_MAX_HASH_LEN];
+ u8 role;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Response");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->initiator || !auth->peer_bi) {
+ dpp_auth_fail(auth, "Unexpected Authentication Response");
+ return NULL;
+ }
+
+ auth->waiting_auth_resp = 0;
+
+ 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");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Unexpected Responder Bootstrapping Key Hash value");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ return NULL;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->own_bi ||
+ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash attribute did not match");
+ return NULL;
+ }
+ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ /* PKEX bootstrapping mandates use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ auth->auth_resp_status = status[0];
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_resp_rx_status(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+ return NULL;
+ }
+
+ if (!i_bootstrap && auth->own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder decided not to use mutual authentication");
+ auth->own_bi = NULL;
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+ auth->own_bi != NULL);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ /* N = pI * PR */
+ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ return NULL;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+ ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+ if (!ctx ||
+ EVP_PKEY_derive_init(ctx) != 1 ||
+ EVP_PKEY_derive_set_peer(ctx, pr) != 1 ||
+ EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+ secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+ EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to derive ECDH shared secret: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ EVP_PKEY_CTX_free(ctx);
+ ctx = NULL;
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ 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->k2, 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;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ if (auth->own_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_initiator(auth) < 0)
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+ if ((auth->allowed_roles ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+ /* Peer selected its role, so move from "either role" to the
+ * role that is compatible with peer's selection. */
+ auth->configurator = role == DPP_CAPAB_ENROLLEE;
+ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+ auth->configurator ? "Configurator" : "Enrollee");
+ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Unexpected role in R-capabilities 0x%02x",
+ role);
+ if (role != DPP_CAPAB_ENROLLEE &&
+ role != DPP_CAPAB_CONFIGURATOR)
+ goto fail;
+ bin_clear_free(unwrapped, unwrapped_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
+ }
+
+ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Secondary Wrapped Data");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped2, wrapped2_len);
+
+ if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+ goto fail;
+
+ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+ unwrapped2 = os_malloc(unwrapped2_len);
+ if (!unwrapped2)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped2, wrapped2_len,
+ 0, NULL, NULL, unwrapped2) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped2, unwrapped2_len);
+
+ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+ dpp_auth_fail(auth,
+ "Invalid attribute in secondary unwrapped data");
+ goto fail;
+ }
+
+ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+ &r_auth_len);
+ if (!r_auth || r_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Responder Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+ r_auth, r_auth_len);
+ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ if (dpp_gen_r_auth(auth, r_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+ r_auth2, r_auth_len);
+ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - Authentication Response in place of Confirm");
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return NULL;
+ return wpabuf_dup(auth->resp_msg);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ EVP_PKEY_free(pr);
+ EVP_PKEY_CTX_free(ctx);
+ return NULL;
+}
+
+
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data,
+ u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *r_nonce;
+ u16 r_nonce_len;
+
+ /* Authentication Confirm failure cases are expected to include
+ * {R-nonce}k2 in the Wrapped Data attribute. */
+
+ 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) {
+ dpp_auth_fail(auth, "Authentication failed");
+ goto fail;
+ }
+ if (aes_siv_decrypt(auth->k2, 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;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+ r_nonce, r_nonce_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+ auth->r_nonce, r_nonce_len);
+ dpp_auth_fail(auth, "R-nonce mismatch");
+ goto fail;
+ }
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE)
+ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+ else if (status == DPP_STATUS_AUTH_FAILURE)
+ dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ i_auth_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ u8 i_auth2[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return -1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (auth->initiator || !auth->own_bi) {
+ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+ return -1;
+ }
+
+ auth->waiting_auth_conf = 0;
+
+ 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");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_auth_fail(auth,
+ "Responder Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->peer_bi ||
+ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+ } else if (auth->peer_bi) {
+ /* Mutual authentication and peer did not include its
+ * Bootstrapping Key Hash attribute. */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+ status[0] == DPP_STATUS_AUTH_FAILURE)
+ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Authentication failed");
+ return -1;
+ }
+
+ 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)
+ return -1;
+ 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;
+ }
+
+ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &i_auth_len);
+ if (!i_auth || i_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Initiator Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+ i_auth, i_auth_len);
+ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ if (dpp_gen_i_auth(auth, i_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+ i_auth2, i_auth_len);
+ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
+ goto fail;
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ dpp_auth_success(auth);
+ return 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+void dpp_configuration_free(struct dpp_configuration *conf)
+{
+ if (!conf)
+ return;
+ str_clear_free(conf->passphrase);
+ os_free(conf->group_id);
+ bin_clear_free(conf, sizeof(*conf));
+}
+
+
+void dpp_auth_deinit(struct dpp_authentication *auth)
+{
+ if (!auth)
+ return;
+ dpp_configuration_free(auth->conf_ap);
+ dpp_configuration_free(auth->conf_sta);
+ EVP_PKEY_free(auth->own_protocol_key);
+ EVP_PKEY_free(auth->peer_protocol_key);
+ wpabuf_free(auth->req_msg);
+ wpabuf_free(auth->resp_msg);
+ wpabuf_free(auth->conf_req);
+ os_free(auth->connector);
+ wpabuf_free(auth->net_access_key);
+ wpabuf_free(auth->c_sign_key);
+ dpp_bootstrap_info_free(auth->tmp_own_bi);
+#ifdef CONFIG_TESTING_OPTIONS
+ os_free(auth->config_obj_override);
+ os_free(auth->discovery_override);
+ os_free(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+ bin_clear_free(auth, sizeof(*auth));
+}
+
+
+static struct wpabuf *
+dpp_build_conf_start(struct dpp_authentication *auth,
+ struct dpp_configuration *conf, size_t tailroom)
+{
+ struct wpabuf *buf;
+ char ssid[6 * sizeof(conf->ssid) + 1];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->discovery_override)
+ tailroom += os_strlen(auth->discovery_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ buf = wpabuf_alloc(200 + tailroom);
+ if (!buf)
+ return NULL;
+ wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":");
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->discovery_override) {
+ wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
+ auth->discovery_override);
+ wpabuf_put_str(buf, auth->discovery_override);
+ wpabuf_put_u8(buf, ',');
+ return buf;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_put_str(buf, "{\"ssid\":\"");
+ json_escape_string(ssid, sizeof(ssid),
+ (const char *) conf->ssid, conf->ssid_len);
+ wpabuf_put_str(buf, ssid);
+ wpabuf_put_str(buf, "\"},");
+
+ return buf;
+}
+
+
+static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve)
+{
+ struct wpabuf *pub;
+ const u8 *pos;
+ char *x = NULL, *y = NULL;
+ int ret = -1;
+
+ pub = dpp_get_pubkey_point(key, 0);
+ if (!pub)
+ goto fail;
+ pos = wpabuf_head(pub);
+ x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+ pos += curve->prime_len;
+ y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0);
+ if (!x || !y)
+ goto fail;
+
+ wpabuf_put_str(buf, "\"");
+ wpabuf_put_str(buf, name);
+ wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\"");
+ wpabuf_put_str(buf, curve->jwk_crv);
+ wpabuf_put_str(buf, "\",\"x\":\"");
+ wpabuf_put_str(buf, x);
+ wpabuf_put_str(buf, "\",\"y\":\"");
+ wpabuf_put_str(buf, y);
+ if (kid) {
+ wpabuf_put_str(buf, "\",\"kid\":\"");
+ wpabuf_put_str(buf, kid);
+ }
+ wpabuf_put_str(buf, "\"}");
+ ret = 0;
+fail:
+ wpabuf_free(pub);
+ os_free(x);
+ os_free(y);
+ return ret;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
+ struct dpp_configuration *conf)
+{
+ struct wpabuf *buf = NULL;
+ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ size_t tailroom;
+ const struct dpp_curve_params *curve;
+ char jws_prot_hdr[100];
+ size_t signed1_len, signed2_len, signed3_len;
+ struct wpabuf *dppcon = NULL;
+ unsigned char *signature = NULL;
+ const unsigned char *p;
+ size_t signature_len;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ char *dot = ".";
+ const EVP_MD *sign_md;
+ const BIGNUM *r, *s;
+ size_t extra_len = 1000;
+
+ if (!auth->conf) {
+ wpa_printf(MSG_INFO,
+ "DPP: No configurator specified - cannot generate DPP config object");
+ goto fail;
+ }
+ curve = auth->conf->curve;
+ if (curve->hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (curve->hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (curve->hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->groups_override)
+ extra_len += os_strlen(auth->groups_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (conf->group_id)
+ extra_len += os_strlen(conf->group_id);
+
+ /* Connector (JSON dppCon object) */
+ dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
+ if (!dppcon)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->groups_override) {
+ wpabuf_put_u8(dppcon, '{');
+ if (auth->groups_override) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: TESTING - groups override: '%s'",
+ auth->groups_override);
+ wpabuf_put_str(dppcon, "\"groups\":");
+ wpabuf_put_str(dppcon, auth->groups_override);
+ wpabuf_put_u8(dppcon, ',');
+ }
+ goto skip_groups;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",",
+ conf->group_id ? conf->group_id : "*");
+ wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta");
+#ifdef CONFIG_TESTING_OPTIONS
+skip_groups:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
+ auth->curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+ goto fail;
+ }
+ if (conf->netaccesskey_expiry) {
+ struct os_tm tm;
+
+ if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to generate expiry string");
+ goto fail;
+ }
+ wpabuf_printf(dppcon,
+ ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"",
+ tm.year, tm.month, tm.day,
+ tm.hour, tm.min, tm.sec);
+ }
+ wpabuf_put_u8(dppcon, '}');
+ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+ (const char *) wpabuf_head(dppcon));
+
+ os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr),
+ "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}",
+ auth->conf->kid, curve->jws_alg);
+ signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr,
+ os_strlen(jws_prot_hdr),
+ &signed1_len, 0);
+ signed2 = (char *) base64_url_encode(wpabuf_head(dppcon),
+ wpabuf_len(dppcon),
+ &signed2_len, 0);
+ if (!signed1 || !signed2)
+ goto fail;
+
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL,
+ auth->conf->csign) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ signature = os_malloc(signature_len);
+ if (!signature)
+ goto fail;
+ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+ signature, signature_len);
+ /* Convert to raw coordinates r,s */
+ p = signature;
+ sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+ if (!sig)
+ goto fail;
+ ECDSA_SIG_get0(sig, &r, &s);
+ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(s, signature + curve->prime_len,
+ curve->prime_len) < 0)
+ goto fail;
+ signature_len = 2 * curve->prime_len;
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+ signature, signature_len);
+ signed3 = (char *) base64_url_encode(signature, signature_len,
+ &signed3_len, 0);
+ if (!signed3)
+ goto fail;
+
+ tailroom = 1000;
+ tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
+ tailroom += signed1_len + signed2_len + signed3_len;
+ buf = dpp_build_conf_start(auth, conf, tailroom);
+ if (!buf)
+ goto fail;
+
+ wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+ wpabuf_put_str(buf, signed1);
+ wpabuf_put_u8(buf, '.');
+ wpabuf_put_str(buf, signed2);
+ wpabuf_put_u8(buf, '.');
+ wpabuf_put_str(buf, signed3);
+ wpabuf_put_str(buf, "\",");
+ if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
+ curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
+ goto fail;
+ }
+
+ wpabuf_put_str(buf, "}}");
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
+ wpabuf_head(buf), wpabuf_len(buf));
+
+out:
+ EVP_MD_CTX_destroy(md_ctx);
+ ECDSA_SIG_free(sig);
+ os_free(signed1);
+ os_free(signed2);
+ os_free(signed3);
+ os_free(signature);
+ wpabuf_free(dppcon);
+ return buf;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
+ wpabuf_free(buf);
+ buf = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
+ struct dpp_configuration *conf)
+{
+ struct wpabuf *buf;
+
+ buf = dpp_build_conf_start(auth, conf, 1000);
+ if (!buf)
+ 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, "\"");
+ }
+ wpabuf_put_str(buf, "}}");
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
+{
+ struct dpp_configuration *conf;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (auth->config_obj_override) {
+ wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
+ return wpabuf_alloc_copy(auth->config_obj_override,
+ os_strlen(auth->config_obj_override));
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ conf = ap ? auth->conf_ap : auth->conf_sta;
+ if (!conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration available for Enrollee(%s) - reject configuration request",
+ ap ? "ap" : "sta");
+ return NULL;
+ }
+
+ if (conf->akm == DPP_AKM_DPP)
+ return dpp_build_conf_obj_dpp(auth, ap, conf);
+ return dpp_build_conf_obj_legacy(auth, ap, conf);
+}
+
+
+static struct wpabuf *
+dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
+ u16 e_nonce_len, int ap)
+{
+ struct wpabuf *conf;
+ size_t clear_len, attr_len;
+ struct wpabuf *clear = NULL, *msg = NULL;
+ u8 *wrapped;
+ const u8 *addr[1];
+ size_t len[1];
+ enum dpp_status_error status;
+
+ conf = dpp_build_conf_obj(auth, ap);
+ if (conf) {
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
+ wpabuf_head(conf), wpabuf_len(conf));
+ }
+ status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+
+ /* { E-nonce, configurationObject}ke */
+ clear_len = 4 + e_nonce_len;
+ if (conf)
+ clear_len += 4 + wpabuf_len(conf);
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = wpabuf_alloc(attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, e_nonce_len);
+ wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
+ wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
+ goto skip_e_nonce;
+ }
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* E-nonce */
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, e_nonce_len);
+ wpabuf_put_data(clear, e_nonce, e_nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+ if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
+ goto skip_config_obj;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (conf) {
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
+ wpabuf_put_le16(clear, wpabuf_len(conf));
+ wpabuf_put_buf(clear, conf);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_config_obj:
+ if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Status");
+ goto skip_status;
+ }
+ if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head(msg);
+ len[0] = wpabuf_len(msg);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+
+ 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),
+ 1, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Configuration Response attributes", msg);
+out:
+ wpabuf_free(conf);
+ wpabuf_free(clear);
+
+ return msg;
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
+ size_t attr_len)
+{
+ const u8 *wrapped_data, *e_nonce, *config_attr;
+ u16 wrapped_data_len, e_nonce_len, config_attr_len;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct wpabuf *resp = NULL;
+ struct json_token *root = NULL, *token;
+ int ap;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Config Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_check_attrs(attr_start, attr_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in config request");
+ return NULL;
+ }
+
+ 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");
+ return NULL;
+ }
+
+ 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)
+ return NULL;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 0, NULL, NULL, 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);
+
+ config_attr = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIG_ATTR_OBJ,
+ &config_attr_len);
+ if (!config_attr) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Config Attributes attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
+ config_attr, config_attr_len);
+
+ root = json_parse((const char *) config_attr, config_attr_len);
+ if (!root) {
+ dpp_auth_fail(auth, "Could not parse Config Attributes");
+ goto fail;
+ }
+
+ token = json_get_member(root, "name");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No Config Attributes - name");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
+
+ token = json_get_member(root, "wi-fi_tech");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
+ if (os_strcmp(token->string, "infra") != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
+ token->string);
+ dpp_auth_fail(auth, "Unsupported wi-fi_tech");
+ goto fail;
+ }
+
+ token = json_get_member(root, "netRole");
+ if (!token || token->type != JSON_STRING) {
+ dpp_auth_fail(auth, "No Config Attributes - netRole");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
+ if (os_strcmp(token->string, "sta") == 0) {
+ ap = 0;
+ } else if (os_strcmp(token->string, "ap") == 0) {
+ ap = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
+ token->string);
+ dpp_auth_fail(auth, "Unsupported netRole");
+ goto fail;
+ }
+
+ resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap);
+
+fail:
+ json_free(root);
+ os_free(unwrapped);
+ return resp;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+ const u8 *prot_hdr, u16 prot_hdr_len,
+ const EVP_MD **ret_md)
+{
+ struct json_token *root, *token;
+ struct wpabuf *kid = NULL;
+
+ root = json_parse((const char *) prot_hdr, prot_hdr_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JSON parsing failed for JWS Protected Header");
+ goto fail;
+ }
+
+ if (root->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JWS Protected Header root is not an object");
+ goto fail;
+ }
+
+ token = json_get_member(root, "typ");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+ token->string);
+ if (os_strcmp(token->string, "dppCon") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header typ=%s",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(root, "alg");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+ token->string);
+ if (os_strcmp(token->string, curve->jws_alg) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+ token->string, curve->jws_alg);
+ goto fail;
+ }
+ if (os_strcmp(token->string, "ES256") == 0 ||
+ os_strcmp(token->string, "BS256") == 0)
+ *ret_md = EVP_sha256();
+ else if (os_strcmp(token->string, "ES384") == 0 ||
+ os_strcmp(token->string, "BS384") == 0)
+ *ret_md = EVP_sha384();
+ else if (os_strcmp(token->string, "ES512") == 0 ||
+ os_strcmp(token->string, "BS512") == 0)
+ *ret_md = EVP_sha512();
+ else
+ *ret_md = NULL;
+ if (!*ret_md) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header alg=%s",
+ token->string);
+ goto fail;
+ }
+
+ kid = json_get_member_base64url(root, "kid");
+ if (!kid) {
+ wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+ kid);
+
+fail:
+ json_free(root);
+ return kid;
+}
+
+
+static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
+ struct json_token *cred)
+{
+ struct json_token *pass, *psk_hex;
+
+ wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
+
+ pass = json_get_member(cred, "pass");
+ psk_hex = json_get_member(cred, "psk_hex");
+
+ if (pass && pass->type == JSON_STRING) {
+ size_t len = os_strlen(pass->string);
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
+ pass->string, len);
+ if (len < 8 || len > 63)
+ return -1;
+ os_strlcpy(auth->passphrase, pass->string,
+ sizeof(auth->passphrase));
+ } else if (psk_hex && psk_hex->type == JSON_STRING) {
+ if (auth->akm == DPP_AKM_SAE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected psk_hex with akm=sae");
+ return -1;
+ }
+ if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
+ hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
+ auth->psk, PMK_LEN);
+ auth->psk_set = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
+ return -1;
+ }
+
+ if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
+ !auth->passphrase[0]) {
+ wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve)
+{
+ struct json_token *token;
+ const struct dpp_curve_params *curve;
+ struct wpabuf *x = NULL, *y = NULL;
+ EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ token = json_get_member(jwk, "kty");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
+ goto fail;
+ }
+ if (os_strcmp(token->string, "EC") != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(jwk, "crv");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
+ goto fail;
+ }
+ curve = dpp_get_curve_jwk_crv(token->string);
+ if (!curve) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
+ token->string);
+ goto fail;
+ }
+
+ x = json_get_member_base64url(jwk, "x");
+ if (!x) {
+ wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
+ if (wpabuf_len(x) != curve->prime_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWK x length %u (expected %u for curve %s)",
+ (unsigned int) wpabuf_len(x),
+ (unsigned int) curve->prime_len, curve->name);
+ goto fail;
+ }
+
+ y = json_get_member_base64url(jwk, "y");
+ if (!y) {
+ wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
+ if (wpabuf_len(y) != curve->prime_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWK y length %u (expected %u for curve %s)",
+ (unsigned int) wpabuf_len(y),
+ (unsigned int) curve->prime_len, curve->name);
+ goto fail;
+ }
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
+ goto fail;
+ }
+
+ pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
+ wpabuf_len(x));
+ *key_curve = curve;
+
+fail:
+ wpabuf_free(x);
+ wpabuf_free(y);
+
+ return pkey;
+}
+
+
+int dpp_key_expired(const char *timestamp, os_time_t *expiry)
+{
+ struct os_time now;
+ unsigned int year, month, day, hour, min, sec;
+ os_time_t utime;
+ const char *pos;
+
+ /* ISO 8601 date and time:
+ * T