aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2018-12-06 05:04:28 +0000
committerCy Schubert <cy@FreeBSD.org>2018-12-06 05:04:28 +0000
commit8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7 (patch)
treeb9a3166587c75d5325dc46c7c83ca435f2e54917
parent765ef8a7642d07aa9616f2b1a9cdebb8e3552f6a (diff)
downloadsrc-8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7.tar.gz
src-8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7.zip
Import wpa_supplicant/hostapd 2.7vendor/wpa/2.7
Notes
Notes: svn path=/vendor/wpa/dist/; revision=341618 svn path=/vendor/wpa/2.7/; revision=341619; tag=vendor/wpa/2.7
-rw-r--r--CONTRIBUTIONS2
-rw-r--r--COPYING2
-rw-r--r--README2
-rw-r--r--hostapd/Android.mk142
-rw-r--r--hostapd/ChangeLog55
-rw-r--r--hostapd/Makefile244
-rw-r--r--hostapd/README30
-rw-r--r--hostapd/android.config17
-rw-r--r--hostapd/config_file.c816
-rw-r--r--hostapd/config_file.h5
-rw-r--r--hostapd/ctrl_iface.c1040
-rw-r--r--hostapd/defconfig38
-rw-r--r--hostapd/hlr_auc_gw.c4
-rw-r--r--hostapd/hostapd.android.rc3
-rw-r--r--hostapd/hostapd.conf449
-rw-r--r--hostapd/hostapd.eap_user_sqlite18
-rw-r--r--hostapd/hostapd_cli.c530
-rw-r--r--hostapd/main.c44
-rw-r--r--hs20/client/est.c6
-rw-r--r--hs20/client/oma_dm_client.c6
-rw-r--r--hs20/client/osu_client.c258
-rw-r--r--hs20/client/osu_client.h1
-rw-r--r--src/ap/Makefile9
-rw-r--r--src/ap/acs.c48
-rw-r--r--src/ap/acs.h5
-rw-r--r--src/ap/ap_config.c163
-rw-r--r--src/ap/ap_config.h143
-rw-r--r--src/ap/ap_drv_ops.c76
-rw-r--r--src/ap/ap_drv_ops.h15
-rw-r--r--src/ap/ap_mlme.c16
-rw-r--r--src/ap/authsrv.c52
-rw-r--r--src/ap/beacon.c96
-rw-r--r--src/ap/beacon.h2
-rw-r--r--src/ap/bss_load.c52
-rw-r--r--src/ap/ctrl_iface_ap.c361
-rw-r--r--src/ap/ctrl_iface_ap.h4
-rw-r--r--src/ap/dfs.c65
-rw-r--r--src/ap/dfs.h5
-rw-r--r--src/ap/dhcp_snoop.c39
-rw-r--r--src/ap/dpp_hostapd.c2096
-rw-r--r--src/ap/dpp_hostapd.h43
-rw-r--r--src/ap/drv_callbacks.c459
-rw-r--r--src/ap/eap_user_db.c2
-rw-r--r--src/ap/eth_p_oui.c191
-rw-r--r--src/ap/eth_p_oui.h28
-rw-r--r--src/ap/fils_hlp.c641
-rw-r--r--src/ap/fils_hlp.h27
-rw-r--r--src/ap/gas_query_ap.c714
-rw-r--r--src/ap/gas_query_ap.h43
-rw-r--r--src/ap/gas_serv.c538
-rw-r--r--src/ap/gas_serv.h10
-rw-r--r--src/ap/hostapd.c400
-rw-r--r--src/ap/hostapd.h94
-rw-r--r--src/ap/hs20.c71
-rw-r--r--src/ap/hs20.h4
-rw-r--r--src/ap/hw_features.c127
-rw-r--r--src/ap/ieee802_11.c1837
-rw-r--r--src/ap/ieee802_11.h31
-rw-r--r--src/ap/ieee802_11_auth.c15
-rw-r--r--src/ap/ieee802_11_auth.h3
-rw-r--r--src/ap/ieee802_11_he.c88
-rw-r--r--src/ap/ieee802_11_ht.c66
-rw-r--r--src/ap/ieee802_11_shared.c200
-rw-r--r--src/ap/ieee802_11_vht.c2
-rw-r--r--src/ap/ieee802_1x.c212
-rw-r--r--src/ap/ieee802_1x.h5
-rw-r--r--src/ap/ndisc_snoop.c1
-rw-r--r--src/ap/neighbor_db.c9
-rw-r--r--src/ap/neighbor_db.h2
-rw-r--r--src/ap/peerkey_auth.c396
-rw-r--r--src/ap/pmksa_cache_auth.c134
-rw-r--r--src/ap/pmksa_cache_auth.h10
-rw-r--r--src/ap/rrm.c164
-rw-r--r--src/ap/rrm.h5
-rw-r--r--src/ap/sta_info.c133
-rw-r--r--src/ap/sta_info.h73
-rw-r--r--src/ap/taxonomy.c1
-rw-r--r--src/ap/tkip_countermeasures.c5
-rw-r--r--src/ap/vlan_init.c14
-rw-r--r--src/ap/wmm.c15
-rw-r--r--src/ap/wnm_ap.c183
-rw-r--r--src/ap/wnm_ap.h3
-rw-r--r--src/ap/wpa_auth.c1880
-rw-r--r--src/ap/wpa_auth.h285
-rw-r--r--src/ap/wpa_auth_ft.c3551
-rw-r--r--src/ap/wpa_auth_glue.c712
-rw-r--r--src/ap/wpa_auth_i.h104
-rw-r--r--src/ap/wpa_auth_ie.c259
-rw-r--r--src/ap/wpa_auth_ie.h14
-rw-r--r--src/ap/wps_hostapd.c4
-rw-r--r--src/common/common_module_tests.c55
-rw-r--r--src/common/ctrl_iface_common.c38
-rw-r--r--src/common/ctrl_iface_common.h6
-rw-r--r--src/common/defs.h70
-rw-r--r--src/common/dhcp.h263
-rw-r--r--src/common/dpp.c7691
-rw-r--r--src/common/dpp.h435
-rw-r--r--src/common/gas.c2
-rw-r--r--src/common/gas.h3
-rw-r--r--src/common/gas_server.c487
-rw-r--r--src/common/gas_server.h44
-rw-r--r--src/common/hw_features_common.c106
-rw-r--r--src/common/hw_features_common.h3
-rw-r--r--src/common/ieee802_11_common.c455
-rw-r--r--src/common/ieee802_11_common.h50
-rw-r--r--src/common/ieee802_11_defs.h528
-rw-r--r--src/common/ieee802_1x_defs.h8
-rw-r--r--src/common/privsep_commands.h25
-rw-r--r--src/common/qca-vendor.h4761
-rw-r--r--src/common/sae.c243
-rw-r--r--src/common/sae.h15
-rw-r--r--src/common/version.h2
-rw-r--r--src/common/wpa_common.c1068
-rw-r--r--src/common/wpa_common.h146
-rw-r--r--src/common/wpa_ctrl.h91
-rw-r--r--src/common/wpa_helpers.c3
-rw-r--r--src/crypto/Makefile5
-rw-r--r--src/crypto/aes-ctr.c28
-rw-r--r--src/crypto/aes-internal-dec.c4
-rw-r--r--src/crypto/aes-internal-enc.c3
-rw-r--r--src/crypto/aes-siv.c62
-rw-r--r--src/crypto/aes.h4
-rw-r--r--src/crypto/aes_siv.h10
-rw-r--r--src/crypto/aes_wrap.h4
-rw-r--r--src/crypto/crypto.h80
-rw-r--r--src/crypto/crypto_gnutls.c216
-rw-r--r--src/crypto/crypto_internal-modexp.c36
-rw-r--r--src/crypto/crypto_libtomcrypt.c49
-rw-r--r--src/crypto/crypto_linux.c1006
-rw-r--r--src/crypto/crypto_module_tests.c434
-rw-r--r--src/crypto/crypto_nettle.c437
-rw-r--r--src/crypto/crypto_none.c3
-rw-r--r--src/crypto/crypto_openssl.c394
-rw-r--r--src/crypto/crypto_wolfssl.c1791
-rw-r--r--src/crypto/des-internal.c17
-rw-r--r--src/crypto/dh_groups.c39
-rw-r--r--src/crypto/fips_prf_wolfssl.c87
-rw-r--r--src/crypto/ms_funcs.c45
-rw-r--r--src/crypto/ms_funcs.h8
-rw-r--r--src/crypto/random.c5
-rw-r--r--src/crypto/sha1-internal.c8
-rw-r--r--src/crypto/sha256-internal.c6
-rw-r--r--src/crypto/sha256-kdf.c20
-rw-r--r--src/crypto/sha384-kdf.c87
-rw-r--r--src/crypto/sha384-prf.c28
-rw-r--r--src/crypto/sha384.c104
-rw-r--r--src/crypto/sha384.h15
-rw-r--r--src/crypto/sha512-kdf.c87
-rw-r--r--src/crypto/sha512-prf.c108
-rw-r--r--src/crypto/sha512.h27
-rw-r--r--src/crypto/tls.h18
-rw-r--r--src/crypto/tls_gnutls.c155
-rw-r--r--src/crypto/tls_internal.c8
-rw-r--r--src/crypto/tls_none.c7
-rw-r--r--src/crypto/tls_openssl.c651
-rw-r--r--src/crypto/tls_wolfssl.c2175
-rw-r--r--src/drivers/driver.h925
-rw-r--r--src/drivers/driver_atheros.c194
-rw-r--r--src/drivers/driver_bsd.c2
-rw-r--r--src/drivers/driver_common.c23
-rw-r--r--src/drivers/driver_hostap.c11
-rw-r--r--src/drivers/driver_macsec_linux.c1310
-rw-r--r--src/drivers/driver_macsec_qca.c659
-rw-r--r--src/drivers/driver_ndis.c16
-rw-r--r--src/drivers/driver_nl80211.c1652
-rw-r--r--src/drivers/driver_nl80211.h28
-rw-r--r--src/drivers/driver_nl80211_capa.c270
-rw-r--r--src/drivers/driver_nl80211_event.c331
-rw-r--r--src/drivers/driver_nl80211_monitor.c11
-rw-r--r--src/drivers/driver_nl80211_scan.c448
-rw-r--r--src/drivers/driver_privsep.c56
-rw-r--r--src/drivers/driver_wext.c42
-rw-r--r--src/drivers/driver_wired.c350
-rw-r--r--src/drivers/driver_wired_common.c322
-rw-r--r--src/drivers/driver_wired_common.h34
-rw-r--r--src/drivers/drivers.c3
-rw-r--r--src/drivers/drivers.mak13
-rw-r--r--src/drivers/drivers.mk12
-rw-r--r--src/drivers/nl80211_copy.h1003
-rw-r--r--src/eap_common/eap_eke_common.c32
-rw-r--r--src/eap_common/eap_fast_common.c2
-rw-r--r--src/eap_common/eap_pwd_common.c339
-rw-r--r--src/eap_common/eap_pwd_common.h19
-rw-r--r--src/eap_common/eap_sim_common.c9
-rw-r--r--src/eap_peer/eap.c368
-rw-r--r--src/eap_peer/eap.h35
-rw-r--r--src/eap_peer/eap_aka.c123
-rw-r--r--src/eap_peer/eap_config.h12
-rw-r--r--src/eap_peer/eap_eke.c12
-rw-r--r--src/eap_peer/eap_fast.c16
-rw-r--r--src/eap_peer/eap_fast_pac.c15
-rw-r--r--src/eap_peer/eap_gpsk.c18
-rw-r--r--src/eap_peer/eap_i.h13
-rw-r--r--src/eap_peer/eap_ikev2.c6
-rw-r--r--src/eap_peer/eap_leap.c17
-rw-r--r--src/eap_peer/eap_mschapv2.c18
-rw-r--r--src/eap_peer/eap_pax.c3
-rw-r--r--src/eap_peer/eap_peap.c12
-rw-r--r--src/eap_peer/eap_proxy.h12
-rw-r--r--src/eap_peer/eap_proxy_dummy.c23
-rw-r--r--src/eap_peer/eap_psk.c12
-rw-r--r--src/eap_peer/eap_pwd.c591
-rw-r--r--src/eap_peer/eap_sake.c12
-rw-r--r--src/eap_peer/eap_sim.c61
-rw-r--r--src/eap_peer/eap_tls.c34
-rw-r--r--src/eap_peer/eap_tls_common.c44
-rw-r--r--src/eap_peer/eap_tls_common.h5
-rw-r--r--src/eap_peer/eap_ttls.c48
-rw-r--r--src/eap_peer/ikev2.c3
-rw-r--r--src/eap_peer/tncc.c6
-rw-r--r--src/eap_server/eap.h6
-rw-r--r--src/eap_server/eap_i.h2
-rw-r--r--src/eap_server/eap_server.c81
-rw-r--r--src/eap_server/eap_server_aka.c6
-rw-r--r--src/eap_server/eap_server_eke.c9
-rw-r--r--src/eap_server/eap_server_fast.c9
-rw-r--r--src/eap_server/eap_server_gpsk.c12
-rw-r--r--src/eap_server/eap_server_gtc.c3
-rw-r--r--src/eap_server/eap_server_ikev2.c5
-rw-r--r--src/eap_server/eap_server_mschapv2.c5
-rw-r--r--src/eap_server/eap_server_pax.c3
-rw-r--r--src/eap_server/eap_server_psk.c12
-rw-r--r--src/eap_server/eap_server_pwd.c429
-rw-r--r--src/eap_server/eap_server_sake.c9
-rw-r--r--src/eap_server/eap_server_sim.c6
-rw-r--r--src/eap_server/eap_server_tls.c19
-rw-r--r--src/eap_server/eap_server_tls_common.c25
-rw-r--r--src/eap_server/eap_server_ttls.c21
-rw-r--r--src/eap_server/eap_server_wsc.c2
-rw-r--r--src/eap_server/eap_tls_common.h7
-rw-r--r--src/eap_server/ikev2.c6
-rw-r--r--src/eap_server/tncs.c6
-rw-r--r--src/eapol_auth/eapol_auth_sm.c17
-rw-r--r--src/eapol_auth/eapol_auth_sm.h1
-rw-r--r--src/eapol_supp/eapol_supp_sm.c113
-rw-r--r--src/eapol_supp/eapol_supp_sm.h50
-rw-r--r--src/fst/fst_ctrl_aux.h22
-rw-r--r--src/fst/fst_ctrl_iface.c2
-rw-r--r--src/fst/fst_group.c2
-rw-r--r--src/fst/fst_iface.h2
-rw-r--r--src/fst/fst_session.c2
-rw-r--r--src/l2_packet/l2_packet.h1
-rw-r--r--src/l2_packet/l2_packet_linux.c29
-rw-r--r--src/l2_packet/l2_packet_privsep.c4
-rw-r--r--src/p2p/p2p.c86
-rw-r--r--src/p2p/p2p.h3
-rw-r--r--src/p2p/p2p_go_neg.c114
-rw-r--r--src/p2p/p2p_group.c2
-rw-r--r--src/p2p/p2p_i.h7
-rw-r--r--src/p2p/p2p_pd.c5
-rw-r--r--src/p2p/p2p_sd.c8
-rw-r--r--src/pae/ieee802_1x_cp.c4
-rw-r--r--src/pae/ieee802_1x_kay.c386
-rw-r--r--src/pae/ieee802_1x_kay.h136
-rw-r--r--src/pae/ieee802_1x_kay_i.h180
-rw-r--r--src/pae/ieee802_1x_secy_ops.c141
-rw-r--r--src/pae/ieee802_1x_secy_ops.h11
-rw-r--r--src/radius/radius.c21
-rw-r--r--src/radius/radius.h10
-rw-r--r--src/radius/radius_client.c4
-rw-r--r--src/radius/radius_das.c217
-rw-r--r--src/radius/radius_das.h5
-rw-r--r--src/radius/radius_server.c654
-rw-r--r--src/radius/radius_server.h5
-rw-r--r--src/rsn_supp/Makefile2
-rw-r--r--src/rsn_supp/peerkey.c1155
-rw-r--r--src/rsn_supp/peerkey.h82
-rw-r--r--src/rsn_supp/pmksa_cache.c135
-rw-r--r--src/rsn_supp/pmksa_cache.h45
-rw-r--r--src/rsn_supp/preauth.c11
-rw-r--r--src/rsn_supp/tdls.c44
-rw-r--r--src/rsn_supp/wpa.c1785
-rw-r--r--src/rsn_supp/wpa.h69
-rw-r--r--src/rsn_supp/wpa_ft.c332
-rw-r--r--src/rsn_supp/wpa_i.h69
-rw-r--r--src/rsn_supp/wpa_ie.c68
-rw-r--r--src/rsn_supp/wpa_ie.h10
-rw-r--r--src/tls/libtommath.c222
-rw-r--r--src/tls/rsa.c2
-rw-r--r--src/tls/tlsv1_client.c2
-rw-r--r--src/tls/tlsv1_client_read.c9
-rw-r--r--src/tls/tlsv1_common.c8
-rw-r--r--src/tls/tlsv1_cred.c6
-rw-r--r--src/tls/tlsv1_server.c2
-rw-r--r--src/tls/x509v3.c15
-rw-r--r--src/utils/Makefile1
-rw-r--r--src/utils/base64.c139
-rw-r--r--src/utils/base64.h4
-rw-r--r--src/utils/browser-wpadebug.c7
-rw-r--r--src/utils/common.c21
-rw-r--r--src/utils/common.h15
-rw-r--r--src/utils/crc32.c85
-rw-r--r--src/utils/crc32.h14
-rw-r--r--src/utils/eloop.h8
-rw-r--r--src/utils/http_curl.c34
-rw-r--r--src/utils/json.c569
-rw-r--r--src/utils/json.h42
-rw-r--r--src/utils/os.h12
-rw-r--r--src/utils/os_none.c6
-rw-r--r--src/utils/os_unix.c15
-rw-r--r--src/utils/os_win32.c10
-rw-r--r--src/utils/trace.c37
-rw-r--r--src/utils/utils_module_tests.c81
-rw-r--r--src/utils/uuid.c25
-rw-r--r--src/utils/uuid.h1
-rw-r--r--src/utils/wpa_debug.c28
-rw-r--r--src/utils/wpa_debug.h3
-rw-r--r--src/utils/wpabuf.c6
-rw-r--r--src/utils/xml-utils.c8
-rw-r--r--src/wps/wps.c14
-rw-r--r--src/wps/wps_common.c1
-rw-r--r--src/wps/wps_er.c9
-rw-r--r--src/wps/wps_registrar.c11
-rw-r--r--wpa_supplicant/Android.mk158
-rw-r--r--wpa_supplicant/ChangeLog70
-rw-r--r--wpa_supplicant/Makefile281
-rw-r--r--wpa_supplicant/README15
-rw-r--r--wpa_supplicant/README-HS2019
-rw-r--r--wpa_supplicant/android.config81
-rw-r--r--wpa_supplicant/ap.c207
-rw-r--r--wpa_supplicant/ap.h21
-rw-r--r--wpa_supplicant/autoscan.c9
-rw-r--r--wpa_supplicant/bgscan.c2
-rw-r--r--wpa_supplicant/bgscan_learn.c3
-rw-r--r--wpa_supplicant/bgscan_simple.c10
-rw-r--r--wpa_supplicant/bss.c64
-rw-r--r--wpa_supplicant/bss.h8
-rw-r--r--wpa_supplicant/config.c393
-rw-r--r--wpa_supplicant/config.h152
-rw-r--r--wpa_supplicant/config_file.c184
-rw-r--r--wpa_supplicant/config_ssid.h197
-rw-r--r--wpa_supplicant/config_winreg.c12
-rw-r--r--wpa_supplicant/ctrl_iface.c1391
-rw-r--r--wpa_supplicant/ctrl_iface_named_pipe.c6
-rw-r--r--wpa_supplicant/ctrl_iface_udp.c4
-rw-r--r--wpa_supplicant/ctrl_iface_unix.c2
-rw-r--r--wpa_supplicant/dbus/dbus_new.c258
-rw-r--r--wpa_supplicant/dbus/dbus_new.h42
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers.c422
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers.h28
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers_p2p.c82
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers_wps.c354
-rw-r--r--wpa_supplicant/defconfig65
-rw-r--r--wpa_supplicant/doc/docbook/eapol_test.84
-rw-r--r--wpa_supplicant/doc/docbook/eapol_test.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_background.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_background.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_cli.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_cli.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_gui.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_gui.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_passphrase.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_passphrase.sgml4
-rw-r--r--wpa_supplicant/doc/docbook/wpa_priv.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_priv.sgml2
-rw-r--r--wpa_supplicant/doc/docbook/wpa_supplicant.84
-rw-r--r--wpa_supplicant/doc/docbook/wpa_supplicant.conf.52
-rw-r--r--wpa_supplicant/doc/docbook/wpa_supplicant.sgml6
-rw-r--r--wpa_supplicant/dpp_supplicant.c2613
-rw-r--r--wpa_supplicant/dpp_supplicant.h39
-rw-r--r--wpa_supplicant/driver_i.h210
-rw-r--r--wpa_supplicant/events.c1048
-rwxr-xr-xwpa_supplicant/examples/dpp-qrcode.py130
-rwxr-xr-xwpa_supplicant/examples/wps-ap-cli6
-rw-r--r--wpa_supplicant/gas_query.c126
-rw-r--r--wpa_supplicant/gas_query.h4
-rw-r--r--wpa_supplicant/hs20_supplicant.c114
-rw-r--r--wpa_supplicant/hs20_supplicant.h3
-rw-r--r--wpa_supplicant/ibss_rsn.c47
-rw-r--r--wpa_supplicant/interworking.c163
-rw-r--r--wpa_supplicant/interworking.h2
-rw-r--r--wpa_supplicant/mbo.c393
-rw-r--r--wpa_supplicant/mesh.c54
-rw-r--r--wpa_supplicant/mesh_mpm.c32
-rw-r--r--wpa_supplicant/mesh_rsn.c67
-rw-r--r--wpa_supplicant/notify.c56
-rw-r--r--wpa_supplicant/notify.h12
-rw-r--r--wpa_supplicant/offchannel.c5
-rw-r--r--wpa_supplicant/op_classes.c325
-rw-r--r--wpa_supplicant/p2p_supplicant.c286
-rw-r--r--wpa_supplicant/preauth_test.c15
-rw-r--r--wpa_supplicant/rrm.c1460
-rw-r--r--wpa_supplicant/scan.c377
-rw-r--r--wpa_supplicant/sme.c813
-rw-r--r--wpa_supplicant/sme.h14
-rw-r--r--wpa_supplicant/wifi_display.c13
-rw-r--r--wpa_supplicant/wmm_ac.c5
-rw-r--r--wpa_supplicant/wnm_sta.c530
-rw-r--r--wpa_supplicant/wnm_sta.h18
-rw-r--r--wpa_supplicant/wpa_cli.c463
-rw-r--r--wpa_supplicant/wpa_passphrase.c8
-rw-r--r--wpa_supplicant/wpa_priv.c265
-rw-r--r--wpa_supplicant/wpa_supplicant.c1764
-rw-r--r--wpa_supplicant/wpa_supplicant.conf192
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h213
-rw-r--r--wpa_supplicant/wpa_supplicant_template.conf1
-rw-r--r--wpa_supplicant/wpas_glue.c169
-rw-r--r--wpa_supplicant/wpas_kay.c166
-rw-r--r--wpa_supplicant/wpas_kay.h10
-rw-r--r--wpa_supplicant/wps_supplicant.c20
400 files changed, 69903 insertions, 10415 deletions
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 <j@w1.fi> and contributors
+Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/COPYING b/COPYING
index 7efce0dee1a7..55815d4016a3 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> 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 <j@w1.fi> and contributors
+Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
These programs are licensed under the BSD license (the one with
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 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 <j@w1.fi> and contributors
+Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> 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<TM> (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 <j@w1.fi>
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -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 <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* 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=<BSSID>,<BSSID Information>,<Operating Class>,
- * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
- */
- 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=<reason>:<reassoc_delay>:<cell_pref>");
- 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=<val> ok=<0/1> buf=<frame hexdump> */
+
+ 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=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
+
+ 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 <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi>
*
* 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 <j@w1.fi>\n"
+ "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n"
"\n"
"usage:\n"
"hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
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=<legacy rate in 100 kbps>
+# HT:
+# beacon_rate=ht:<HT MCS>
+# VHT:
+# beacon_rate=vht:<VHT MCS>
+#
+# 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_<queue name>_<param>
-# 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
# <station count>:<channel utilization>:<available admission capacity>
#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> <shared secret>
+# 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:
+#<password/credential>[|mac=<peer mac>][|id=<identifier>]
+# Examples:
+#sae_password=secret
+#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
+#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier
+
# SAE 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=<bssid>
+# SSID in same format as ssid2 described above.
+#owe_transition_ssid=<SSID>
+# Alternatively, OWE transition mode BSSID/SSID can be configured with a
+# reference to a BSS operated by this hostapd process.
+#owe_transition_ifname=<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: <MAC address> <NAS Identifier> <128-bit key as hex string>
+# format: <MAC address> <NAS Identifier> <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: <MAC address> <R1KH-ID> <128-bit key as hex string>
+# format: <MAC address> <R1KH-ID> <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
# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
#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 <j@w1.fi>
+ * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,7 +21,7 @@
static const char *const hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> 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,
"<addr> = 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,
"<addr> = add a new station" },
{ "deauthenticate", hostapd_cli_cmd_deauthenticate,
- hostapd_complete_deauthenticate,
+ hostapd_complete_stations,
"<addr> = deauthenticate a station" },
{ "disassociate", hostapd_cli_cmd_disassociate,
- hostapd_complete_disassociate,
+ hostapd_complete_stations,
"<addr> = disassociate a station" },
#ifdef CONFIG_TAXONOMY
- { "signature", hostapd_cli_cmd_signature, NULL,
+ { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
"<addr> = 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,
"<addr> = 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,
+ "<params...> = send FST-MANAGER control interface command" },
#endif /* CONFIG_FST */
- { "raw", hostapd_cli_cmd_raw, NULL, NULL },
+ { "raw", hostapd_cli_cmd_raw, NULL,
+ "<params..> = send unprocessed command" },
{ "level", hostapd_cli_cmd_level, NULL,
"<debug level> = 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,
+ "<name> <value> = set runtime variables" },
+ { "get", hostapd_cli_cmd_get, hostapd_complete_get,
+ "<name> = get runtime info" },
+ { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL,
+ "<arg,arg,...> = set QoS Map set element" },
+ { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf,
+ hostapd_complete_stations,
+ "<addr> = send QoS Map Configure frame" },
+ { "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+ "<cs_count> <freq> [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,
+ "<addr> <url>\n"
+ " = send WNM-Notification Subscription Remediation Request" },
+ { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL,
+ "<addr> <code (0/1)> <Re-auth-Delay(sec)> [url]\n"
+ " = send WNM-Notification imminent deauthentication indication" },
+ { "vendor", hostapd_cli_cmd_vendor, NULL,
+ "<vendor id> <sub command id> [<hex formatted data>]\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,
+ "<addr> <ssid=> <nr=> [lci=] [civic=] [stat]\n"
+ " = add AP to neighbor database" },
+ { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
+ "<addr> <ssid=> = remove AP from neighbor database" },
+ { "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
+ "<addr> = 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=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
+ { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
+ "*|<id> = remove DPP bootstrap information" },
+ { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
+ "<id> = get DPP bootstrap URI" },
+ { "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
+ "<id> = show DPP bootstrap information" },
+ { "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
+ "peer=<id> [own=<id>] = initiate DPP bootstrapping" },
+ { "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
+ "<freq in MHz> = 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,
+ "*|<id> = remove DPP configurator" },
+ { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key,
+ NULL,
+ "<id> = 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,
+ "*|<id> = 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,
+ "<addr> = 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 <j@w1.fi>
+ * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi>
*
* 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 <j@w1.fi> "
+ "Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> "
"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/<fqdn>, 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/<fqdn> 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, "</table></a><br><small>BSSID: %s<br>\n"
"SSID: %s<br>\n",
last->bssid, last->osu_ssid);
+ if (last->osu_ssid2[0])
+ fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2);
if (last->osu_nai[0])
fprintf(f, "NAI: %s<br>\n", last->osu_nai);
+ if (last->osu_nai2[0])
+ fprintf(f, "NAI2: %s<br>\n", last->osu_nai2);
fprintf(f, "URL: %s<br>\n"
"methods:%s%s<br>\n"
"</small></p>\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;
- }
-
- 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 (devinfo) {
+ 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);
+ }
}
- }
- 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)
}
-void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
{
- struct hostapd_eap_user *user, *prev_user;
+#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)
+{
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(&params, 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,
+ &params.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, &params);
}
@@ -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:
+ * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
+ */
+
+ 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:
+ * <BSSID> <PMKID> <PMK> <expiration in seconds>
+ */
+
+ 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 <j@w1.fi>
- * 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 <j@w1.fi>
- * 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 <netinet/ip.h>
-#include <netinet/udp.h>
#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 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "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 <j@w1.fi>
+ *
+ * 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 <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "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,
+ &center_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 <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* 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 (sta->sae->tmp)
+ rx_id = sta->sae->tmp->pw_id;
- if (hapd->conf->ssid.wpa_passphrase == NULL) {
+ 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");
re